node-tao 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +230 -0
  2. package/checks.d.ts +8 -0
  3. package/checks.d.ts.map +1 -0
  4. package/checks.js +37 -0
  5. package/checks.js.map +1 -0
  6. package/const.d.ts +24 -0
  7. package/const.d.ts.map +1 -0
  8. package/const.js +51 -0
  9. package/const.js.map +1 -0
  10. package/default-config.d.ts +4 -0
  11. package/default-config.d.ts.map +1 -0
  12. package/default-config.js +25 -0
  13. package/default-config.js.map +1 -0
  14. package/error/error.html +259 -0
  15. package/error-utils.d.ts +21 -0
  16. package/error-utils.d.ts.map +1 -0
  17. package/error-utils.js +163 -0
  18. package/error-utils.js.map +1 -0
  19. package/index.d.ts +3 -0
  20. package/index.d.ts.map +1 -0
  21. package/index.js +6 -0
  22. package/index.js.map +1 -0
  23. package/init.d.ts +12 -0
  24. package/init.d.ts.map +1 -0
  25. package/init.js +31 -0
  26. package/init.js.map +1 -0
  27. package/interfaces.d.ts +170 -0
  28. package/interfaces.d.ts.map +1 -0
  29. package/interfaces.js +3 -0
  30. package/interfaces.js.map +1 -0
  31. package/metrics.d.ts +16 -0
  32. package/metrics.d.ts.map +1 -0
  33. package/metrics.js +80 -0
  34. package/metrics.js.map +1 -0
  35. package/package.json +45 -0
  36. package/parsing-helpers.d.ts +8 -0
  37. package/parsing-helpers.d.ts.map +1 -0
  38. package/parsing-helpers.js +60 -0
  39. package/parsing-helpers.js.map +1 -0
  40. package/store.d.ts +29 -0
  41. package/store.d.ts.map +1 -0
  42. package/store.js +52 -0
  43. package/store.js.map +1 -0
  44. package/tao.d.ts +81 -0
  45. package/tao.d.ts.map +1 -0
  46. package/tao.js +533 -0
  47. package/tao.js.map +1 -0
  48. package/templates-access.d.ts +15 -0
  49. package/templates-access.d.ts.map +1 -0
  50. package/templates-access.js +38 -0
  51. package/templates-access.js.map +1 -0
  52. package/utils.d.ts +45 -0
  53. package/utils.d.ts.map +1 -0
  54. package/utils.js +227 -0
  55. package/utils.js.map +1 -0
package/README.md ADDED
@@ -0,0 +1,230 @@
1
+ ## Node Tao
2
+
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/GreenFlag31/node-tao/main/node-tao.jpg" alt="node-tao template engine representation" width="300" height="300"/>
5
+
6
+ </p>
7
+
8
+ **`TAO`** is a simple, lightweight and very fast embedded JS templating. It emphasizes great performance, security, and developer experience.
9
+
10
+ ### 🌟 Features
11
+
12
+ - 🚀 Super Fast
13
+ - 🔧 Configurable
14
+ - 🔥 Caching
15
+ - ⚡️ Support for partials
16
+ - 📝 Easy template syntax (no prefix needed)
17
+ - 💻 Developer experience
18
+ - 🧩 Support for local and global helpers
19
+ - 🛡️ Security by design
20
+
21
+ ## Get Started
22
+
23
+ Define a template `simple.html` inside a view directory `templates`
24
+
25
+ ```html
26
+ <!-- templates/simple.html -->
27
+ <h1>Hi <%= name %>!</h1>
28
+ ```
29
+
30
+ ```javascript
31
+ import { Tao } from 'tao';
32
+
33
+ const tao = new Tao({ views: path.join(__dirname, 'templates') });
34
+
35
+ const res = tao.render('simple', { name: 'Tao' });
36
+ console.log(res); // <h1>Hi Tao!</h1>
37
+ ```
38
+
39
+ ## Helpers
40
+
41
+ Helpers are functions that can be used inside a template. Helpers can be **local** (only available in a particular `render`) or **global** (available everywhere on the instance).
42
+
43
+ ```javascript
44
+ import { Tao } from 'tao';
45
+
46
+ const tao = new Tao({ views: path.join(__dirname, 'templates') });
47
+
48
+ // Global helper
49
+ function nPlusTwo(n: number) {
50
+ return n + 2;
51
+ }
52
+ // Global helper need to be registered on the instance
53
+ tao.defineHelpers({ nPlusTwo });
54
+
55
+ // Render a template
56
+ app.get('/', (req, res) => {
57
+ // Local helper
58
+ function nPlusOne(n: number) {
59
+ return n + 1;
60
+ }
61
+
62
+ const res = tao.render('simple', { name: 'Ben' }, { nPlusOne });
63
+ console.log(res); // <h1>Hi Ben!</h1>
64
+ });
65
+ ```
66
+
67
+ Usage:
68
+
69
+ ```javascript
70
+ // simple.html
71
+ <%= nPlusOne(1) %>
72
+ ```
73
+
74
+ NB: _Always escape (=) the output of a helper function when it includes user-controlled data._
75
+
76
+ It is also possible to register helpers on `globalThis` without providing them to the template engine, but it can lead to name collision.
77
+
78
+ ## Include
79
+
80
+ In your template, you might want to include other templates:
81
+
82
+ ```html
83
+ <h1>Hi <%= name %>!</h1>
84
+ <!-- include "article" template and provide data -->
85
+ <%~ include('article', { phone: 'Tao T9' }) %>
86
+ ```
87
+
88
+ Child components will inherit from **data** and **helpers** provided in the parent component.
89
+
90
+ ## Template prefix options
91
+
92
+ There are three differents template prefixes:
93
+
94
+ ```html
95
+ <!-- Evaluation (''): no escape (ideal for js execution) -->
96
+ <% const age = 33; %>
97
+ <!-- Interpolation (=): escaping (ideal for data interpolation) -->
98
+ <p><%= `Age: ${age}` %></p>
99
+ <!-- Raw (~): no escape (ideal for HTML inclusion) -->
100
+ <%~ include('product') %>
101
+ ```
102
+
103
+ NB: _Those prefix are configurable in the options._
104
+
105
+ ## Template paths resolution
106
+
107
+ `TAO` will recursively add all templates matching the containing `views` path definition
108
+
109
+ ```javascript
110
+ import { Tao } from 'tao';
111
+
112
+ const tao = new Tao({ views: path.join(__dirname, 'templates') });
113
+ ```
114
+
115
+ ...such that following structure is ok :
116
+
117
+ ```
118
+ | /templates
119
+ | - simple.html ✔️
120
+ | /products
121
+ | - article.html ✔️
122
+ | /...
123
+ | - nested.html ✔️
124
+ ```
125
+
126
+ By default, `fileResolution` is set to `flexible`, which means that you can just provide the _unique end of the path_:
127
+
128
+ ```javascript
129
+ const res = tao.render('nested'); // accepted ✔️
130
+ const res = tao.render('products/.../nested'); // not necessary ❌
131
+ ```
132
+
133
+ `TAO` will successfully identify the nested templates without providing the subfolder(s).
134
+
135
+ ## Programmatically defined templates
136
+
137
+ You might want to define programmatically templates:
138
+
139
+ ```javascript
140
+ const headerPartial = `
141
+ <header>
142
+ <h1><%= title %></h1>
143
+ </header>
144
+ `;
145
+
146
+ tao.loadTemplate('@header', headerPartial);
147
+ const rendered = tao.render('@header', { title: 'Computer shop' });
148
+ ```
149
+
150
+ ## Cache Storage
151
+
152
+ `TAO` uses cache stores to manage caching. You might want to interact with those stores to retrieve or delete an entry:
153
+
154
+ ```javascript
155
+ tao.helpersStore.remove('myHelperFn');
156
+ ```
157
+
158
+ ## Security by design
159
+
160
+ By default, `TAO` assume you are running your app in production, so no error will be thrown, such that error stack traces are not visible in your browser. Errors will be displayed in your editor console, and visual error representation (see developer experience) is available in your browser by setting `debug: true` at option initialisation.
161
+
162
+ ## Developer experience
163
+
164
+ All methods, properties are correctly typed and documented, so you should get help from your editor.
165
+
166
+ In case of an error, a visual representation is available in your browser, giving you all the details and the precise line of the error (if available).
167
+
168
+ ![Error representation](https://raw.githubusercontent.com/GreenFlag31/node-tao/main/error-representation.png)
169
+
170
+ NB: _set `debug: true` to activate this option. Do not activate this option in production._
171
+
172
+ Metrics are also available, so you get usefull informations about the template rendering time, cache hit, mapped templates, etc. in your browser console.
173
+
174
+ ![Metrics](https://raw.githubusercontent.com/GreenFlag31/node-tao/main/metrics.png)
175
+
176
+ If you want to inspect the data provided to the template, it is available directly in your browser console under the object `data`.
177
+
178
+ ![Data](https://raw.githubusercontent.com/GreenFlag31/node-tao/main/data.png)
179
+
180
+ NB: _set `metrics: true` to activate this option. Do not activate this option in production._
181
+
182
+ ## FAQs
183
+
184
+ <details>
185
+ <summary>
186
+ <b>Some words about this library</b>
187
+ </summary>
188
+
189
+ It started as a fork of `eta`, but became a dedicated library because the changes made were too significant. Some parts are still based on `eta`, especially the template parsing, and if you know `eta`, the API will be familiar.
190
+
191
+ </details>
192
+
193
+ <details>
194
+ <summary>
195
+ <b>If you want to compare tao with eta</b>
196
+ </summary>
197
+
198
+ - **Tao set security by design**: Stack traces are not visible in the browser. Increased security in files mapping.
199
+ - **Increased developer experience**: Visual error representation, metrics, configuration options are checked.
200
+ - **Immutability**: Data provided in the template is immutable, ie. template data modification does not affect original data.
201
+ - **Clearer API**: Scope is well defined and restricted, which also improves security. Clean code practices are enforced.
202
+ - **Clearer template syntax**: No prefix are needed.
203
+ - **Helpers**: Global and local helpers, which are clearer and more suitable for little template logic.
204
+ - **Flexible template path resolution**: With `fileResolution` mode set to `flexible`, only end unique paths can be provided, which increases file path readability (aka. `namespaces`).
205
+ - **Performance**: Various performance optimization.
206
+
207
+ </details>
208
+
209
+ <details>
210
+ <summary>
211
+ <b>Choices</b>
212
+ </summary>
213
+
214
+ - **No async support**: Supporting async rendering (e.g., `await include`) within templates encourages placing too much logic in the view layer and can be considered as an _anti-pattern_. Templates should be responsible for displaying data, while controllers should handle logic. Async behavior in templates would also require error handling (e.g., `try/catch`), adding complexity and possible errors. Async logic in template make it impossible de optimize through a `Promise.all` or any parallelism, hard to test and debug. In short: if you have async data, fetch it beforehand and render it synchronously.
215
+
216
+ - **No `layouts`**: Layouts are essentially includes and add unnecessary complexity to the rendering process.
217
+
218
+ - **No `rmWhitespace`**: Stripping whitespace at the template level yields negligible HTML size savings. Using compression (e.g., via Nginx or other proxies) is far more effective and scalable.
219
+
220
+ _If you think those features are absolutely necessary, please open a new discussion on github and provide an example._
221
+
222
+ </details>
223
+
224
+ <br />
225
+
226
+ ## Change logs
227
+
228
+ ## Credits
229
+
230
+ - Syntax and some parts of compilation are based on `eta`.
package/checks.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { Parse, Tags } from './interfaces';
2
+ import { getPathWithExtension } from './utils';
3
+ declare function isTemplateDynamicallyDefined(template: string): boolean;
4
+ declare function checkOpeningAndClosingTag(tags: Tags): void;
5
+ declare function checkPrefixTemplateTags(parse: Parse): void;
6
+ declare function trimDotFromExtension(extension: string): string;
7
+ export { getPathWithExtension, isTemplateDynamicallyDefined, checkOpeningAndClosingTag, checkPrefixTemplateTags, trimDotFromExtension, };
8
+ //# sourceMappingURL=checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../src/checks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,iBAAS,4BAA4B,CAAC,QAAQ,EAAE,MAAM,WAErD;AAED,iBAAS,yBAAyB,CAAC,IAAI,EAAE,IAAI,QAK5C;AAED,iBAAS,uBAAuB,CAAC,KAAK,EAAE,KAAK,QAgB5C;AAED,iBAAS,oBAAoB,CAAC,SAAS,EAAE,MAAM,UAI9C;AAED,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,GACrB,CAAC"}
package/checks.js ADDED
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trimDotFromExtension = exports.checkPrefixTemplateTags = exports.checkOpeningAndClosingTag = exports.isTemplateDynamicallyDefined = exports.getPathWithExtension = void 0;
4
+ const const_1 = require("./const");
5
+ const utils_1 = require("./utils");
6
+ Object.defineProperty(exports, "getPathWithExtension", { enumerable: true, get: function () { return utils_1.getPathWithExtension; } });
7
+ function isTemplateDynamicallyDefined(template) {
8
+ return template.startsWith(const_1.DYNAMICAL_TEMPLATE_PREFIX);
9
+ }
10
+ exports.isTemplateDynamicallyDefined = isTemplateDynamicallyDefined;
11
+ function checkOpeningAndClosingTag(tags) {
12
+ const { opening, closing } = tags;
13
+ if (opening === closing) {
14
+ throw new Error('Opening and closing tag should be different');
15
+ }
16
+ }
17
+ exports.checkOpeningAndClosingTag = checkOpeningAndClosingTag;
18
+ function checkPrefixTemplateTags(parse) {
19
+ const parseValues = Object.entries(parse);
20
+ for (let i = 0; i < parseValues.length; i++) {
21
+ const [key, value] = parseValues[i];
22
+ for (let j = i + 1; j < parseValues.length; j++) {
23
+ const [nextKey, nextValue] = parseValues[j];
24
+ if (value === nextValue) {
25
+ throw new Error(`Cannot have the same parse value at '${key}' and '${nextKey}' with value '${value}'`);
26
+ }
27
+ }
28
+ }
29
+ }
30
+ exports.checkPrefixTemplateTags = checkPrefixTemplateTags;
31
+ function trimDotFromExtension(extension) {
32
+ if (extension.startsWith('.'))
33
+ return extension.slice(1);
34
+ return extension;
35
+ }
36
+ exports.trimDotFromExtension = trimDotFromExtension;
37
+ //# sourceMappingURL=checks.js.map
package/checks.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../src/checks.ts"],"names":[],"mappings":";;;AAAA,mCAAoD;AAEpD,mCAA+C;AAsC7C,qGAtCO,4BAAoB,OAsCP;AApCtB,SAAS,4BAA4B,CAAC,QAAgB;IACpD,OAAO,QAAQ,CAAC,UAAU,CAAC,iCAAyB,CAAC,CAAC;AACxD,CAAC;AAmCC,oEAA4B;AAjC9B,SAAS,yBAAyB,CAAC,IAAU;IAC3C,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAClC,IAAI,OAAO,KAAK,OAAO,EAAE;QACvB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;KAChE;AACH,CAAC;AA6BC,8DAAyB;AA3B3B,SAAS,uBAAuB,CAAC,KAAY;IAC3C,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAE1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QAC3C,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAEpC,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAC/C,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAE5C,IAAI,KAAK,KAAK,SAAS,EAAE;gBACvB,MAAM,IAAI,KAAK,CACb,wCAAwC,GAAG,UAAU,OAAO,iBAAiB,KAAK,GAAG,CACtF,CAAC;aACH;SACF;KACF;AACH,CAAC;AAYC,0DAAuB;AAVzB,SAAS,oBAAoB,CAAC,SAAiB;IAC7C,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEzD,OAAO,SAAS,CAAC;AACnB,CAAC;AAOC,oDAAoB"}
package/const.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ declare const DEFAULT_EXTENSION = "html";
2
+ declare const DEFAULT_OPENING = "<%";
3
+ declare const DEFAULT_CLOSING = "%>";
4
+ declare const DEFAULT_EXEC = "";
5
+ declare const DEFAULT_INTERPOLATE = "=";
6
+ declare const DEFAULT_RAW = "~";
7
+ declare const TEMPLATE_VARNAME = "it";
8
+ declare const HELPER_VARNAME = "hp";
9
+ declare const TP_VARNAME_WITH_PREFIX: string;
10
+ declare const HELPER_WITH_PREFIX: string;
11
+ declare const DYNAMICAL_TEMPLATE_PREFIX = "@";
12
+ declare const PLACEHOLDER_VAR_START = "\"<injected-var-start>\";";
13
+ declare const PLACEHOLDER_VAR_END = "\"<injected-var-end>\";";
14
+ declare const LITERAL_REGEX: RegExp;
15
+ declare const SINGLE_QUOTE_REGEX: RegExp;
16
+ declare const DOUBLE_QUOTE_REGEX: RegExp;
17
+ /**
18
+ * A map of special HTML characters to their XML-escaped equivalents
19
+ */
20
+ declare const ESC_MAP: {
21
+ [key: string]: string;
22
+ };
23
+ export { DEFAULT_EXTENSION, LITERAL_REGEX, SINGLE_QUOTE_REGEX, DOUBLE_QUOTE_REGEX, ESC_MAP, TEMPLATE_VARNAME, DYNAMICAL_TEMPLATE_PREFIX, TP_VARNAME_WITH_PREFIX, HELPER_WITH_PREFIX, HELPER_VARNAME, DEFAULT_CLOSING, DEFAULT_OPENING, DEFAULT_EXEC, DEFAULT_INTERPOLATE, DEFAULT_RAW, PLACEHOLDER_VAR_START, PLACEHOLDER_VAR_END, };
24
+ //# sourceMappingURL=const.d.ts.map
package/const.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"const.d.ts","sourceRoot":"","sources":["../src/const.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,iBAAiB,SAAS,CAAC;AACjC,QAAA,MAAM,eAAe,OAAO,CAAC;AAC7B,QAAA,MAAM,eAAe,OAAO,CAAC;AAE7B,QAAA,MAAM,YAAY,KAAK,CAAC;AACxB,QAAA,MAAM,mBAAmB,MAAM,CAAC;AAChC,QAAA,MAAM,WAAW,MAAM,CAAC;AAExB,QAAA,MAAM,gBAAgB,OAAO,CAAC;AAC9B,QAAA,MAAM,cAAc,OAAO,CAAC;AAO5B,QAAA,MAAM,sBAAsB,QAAuC,CAAC;AACpE,QAAA,MAAM,kBAAkB,QAAqC,CAAC;AAC9D,QAAA,MAAM,yBAAyB,MAAM,CAAC;AAEtC,QAAA,MAAM,qBAAqB,8BAA4B,CAAC;AACxD,QAAA,MAAM,mBAAmB,4BAA0B,CAAC;AAEpD,QAAA,MAAM,aAAa,QAAuE,CAAC;AAC3F,QAAA,MAAM,kBAAkB,QAAsC,CAAC;AAC/D,QAAA,MAAM,kBAAkB,QAAsC,CAAC;AAE/D;;GAEG;AACH,QAAA,MAAM,OAAO,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAMrC,CAAC;AAEF,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,OAAO,EACP,gBAAgB,EAChB,yBAAyB,EACzB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,mBAAmB,GACpB,CAAC"}
package/const.js ADDED
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PLACEHOLDER_VAR_END = exports.PLACEHOLDER_VAR_START = exports.DEFAULT_RAW = exports.DEFAULT_INTERPOLATE = exports.DEFAULT_EXEC = exports.DEFAULT_OPENING = exports.DEFAULT_CLOSING = exports.HELPER_VARNAME = exports.HELPER_WITH_PREFIX = exports.TP_VARNAME_WITH_PREFIX = exports.DYNAMICAL_TEMPLATE_PREFIX = exports.TEMPLATE_VARNAME = exports.ESC_MAP = exports.DOUBLE_QUOTE_REGEX = exports.SINGLE_QUOTE_REGEX = exports.LITERAL_REGEX = exports.DEFAULT_EXTENSION = void 0;
4
+ const DEFAULT_EXTENSION = 'html';
5
+ exports.DEFAULT_EXTENSION = DEFAULT_EXTENSION;
6
+ const DEFAULT_OPENING = '<%';
7
+ exports.DEFAULT_OPENING = DEFAULT_OPENING;
8
+ const DEFAULT_CLOSING = '%>';
9
+ exports.DEFAULT_CLOSING = DEFAULT_CLOSING;
10
+ const DEFAULT_EXEC = '';
11
+ exports.DEFAULT_EXEC = DEFAULT_EXEC;
12
+ const DEFAULT_INTERPOLATE = '=';
13
+ exports.DEFAULT_INTERPOLATE = DEFAULT_INTERPOLATE;
14
+ const DEFAULT_RAW = '~';
15
+ exports.DEFAULT_RAW = DEFAULT_RAW;
16
+ const TEMPLATE_VARNAME = 'it';
17
+ exports.TEMPLATE_VARNAME = TEMPLATE_VARNAME;
18
+ const HELPER_VARNAME = 'hp';
19
+ exports.HELPER_VARNAME = HELPER_VARNAME;
20
+ /**
21
+ * Internal variable signal.
22
+ */
23
+ const PRIVATE_ONLY = 'ɵɵ';
24
+ const TP_VARNAME_WITH_PREFIX = `${PRIVATE_ONLY}${TEMPLATE_VARNAME}`;
25
+ exports.TP_VARNAME_WITH_PREFIX = TP_VARNAME_WITH_PREFIX;
26
+ const HELPER_WITH_PREFIX = `${PRIVATE_ONLY}${HELPER_VARNAME}`;
27
+ exports.HELPER_WITH_PREFIX = HELPER_WITH_PREFIX;
28
+ const DYNAMICAL_TEMPLATE_PREFIX = '@';
29
+ exports.DYNAMICAL_TEMPLATE_PREFIX = DYNAMICAL_TEMPLATE_PREFIX;
30
+ const PLACEHOLDER_VAR_START = '"<injected-var-start>";';
31
+ exports.PLACEHOLDER_VAR_START = PLACEHOLDER_VAR_START;
32
+ const PLACEHOLDER_VAR_END = '"<injected-var-end>";';
33
+ exports.PLACEHOLDER_VAR_END = PLACEHOLDER_VAR_END;
34
+ const LITERAL_REGEX = /`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})*}|(?!\${)[^\\`])*`/g;
35
+ exports.LITERAL_REGEX = LITERAL_REGEX;
36
+ const SINGLE_QUOTE_REGEX = /'(?:\\[\s\w"'\\`]|[^\n\r'\\])*?'/g;
37
+ exports.SINGLE_QUOTE_REGEX = SINGLE_QUOTE_REGEX;
38
+ const DOUBLE_QUOTE_REGEX = /"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g;
39
+ exports.DOUBLE_QUOTE_REGEX = DOUBLE_QUOTE_REGEX;
40
+ /**
41
+ * A map of special HTML characters to their XML-escaped equivalents
42
+ */
43
+ const ESC_MAP = {
44
+ '&': '&amp;',
45
+ '<': '&lt;',
46
+ '>': '&gt;',
47
+ '"': '&quot;',
48
+ "'": '&#39;',
49
+ };
50
+ exports.ESC_MAP = ESC_MAP;
51
+ //# sourceMappingURL=const.js.map
package/const.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"const.js","sourceRoot":"","sources":["../src/const.ts"],"names":[],"mappings":";;;AAAA,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAuC/B,8CAAiB;AAtCnB,MAAM,eAAe,GAAG,IAAI,CAAC;AAiD3B,0CAAe;AAhDjB,MAAM,eAAe,GAAG,IAAI,CAAC;AA+C3B,0CAAe;AA7CjB,MAAM,YAAY,GAAG,EAAE,CAAC;AA+CtB,oCAAY;AA9Cd,MAAM,mBAAmB,GAAG,GAAG,CAAC;AA+C9B,kDAAmB;AA9CrB,MAAM,WAAW,GAAG,GAAG,CAAC;AA+CtB,kCAAW;AA7Cb,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAoC5B,4CAAgB;AAnClB,MAAM,cAAc,GAAG,IAAI,CAAC;AAuC1B,wCAAc;AArChB;;GAEG;AACH,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,MAAM,sBAAsB,GAAG,GAAG,YAAY,GAAG,gBAAgB,EAAE,CAAC;AA8BlE,wDAAsB;AA7BxB,MAAM,kBAAkB,GAAG,GAAG,YAAY,GAAG,cAAc,EAAE,CAAC;AA8B5D,gDAAkB;AA7BpB,MAAM,yBAAyB,GAAG,GAAG,CAAC;AA2BpC,8DAAyB;AAzB3B,MAAM,qBAAqB,GAAG,yBAAyB,CAAC;AAkCtD,sDAAqB;AAjCvB,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;AAkClD,kDAAmB;AAhCrB,MAAM,aAAa,GAAG,oEAAoE,CAAC;AAiBzF,sCAAa;AAhBf,MAAM,kBAAkB,GAAG,mCAAmC,CAAC;AAiB7D,gDAAkB;AAhBpB,MAAM,kBAAkB,GAAG,mCAAmC,CAAC;AAiB7D,gDAAkB;AAfpB;;GAEG;AACH,MAAM,OAAO,GAA8B;IACzC,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,OAAO;CACb,CAAC;AAOA,0BAAO"}
@@ -0,0 +1,4 @@
1
+ import { DefinitiveOptions } from './interfaces';
2
+ declare const defaultConfig: DefinitiveOptions;
3
+ export { defaultConfig };
4
+ //# sourceMappingURL=default-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-config.d.ts","sourceRoot":"","sources":["../src/default-config.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGjD,QAAA,MAAM,aAAa,EAAE,iBAiBpB,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultConfig = void 0;
4
+ const const_1 = require("./const");
5
+ const utils_1 = require("./utils");
6
+ const defaultConfig = {
7
+ views: process.cwd(),
8
+ autoEscape: true,
9
+ escapeFunction: utils_1.XMLEscape,
10
+ cache: true,
11
+ development: false,
12
+ fileResolution: 'flexible',
13
+ parse: {
14
+ exec: const_1.DEFAULT_EXEC,
15
+ interpolate: const_1.DEFAULT_INTERPOLATE,
16
+ raw: const_1.DEFAULT_RAW,
17
+ },
18
+ tags: {
19
+ opening: const_1.DEFAULT_OPENING,
20
+ closing: const_1.DEFAULT_CLOSING,
21
+ },
22
+ extension: const_1.DEFAULT_EXTENSION,
23
+ };
24
+ exports.defaultConfig = defaultConfig;
25
+ //# sourceMappingURL=default-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-config.js","sourceRoot":"","sources":["../src/default-config.ts"],"names":[],"mappings":";;;AAAA,mCAOiB;AAEjB,mCAAoC;AAEpC,MAAM,aAAa,GAAsB;IACvC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE;IACpB,UAAU,EAAE,IAAI;IAChB,cAAc,EAAE,iBAAS;IACzB,KAAK,EAAE,IAAI;IACX,WAAW,EAAE,KAAK;IAClB,cAAc,EAAE,UAAU;IAC1B,KAAK,EAAE;QACL,IAAI,EAAE,oBAAY;QAClB,WAAW,EAAE,2BAAmB;QAChC,GAAG,EAAE,mBAAW;KACjB;IACD,IAAI,EAAE;QACJ,OAAO,EAAE,uBAAe;QACxB,OAAO,EAAE,uBAAe;KACzB;IACD,SAAS,EAAE,yBAAiB;CAC7B,CAAC;AAEO,sCAAa"}
@@ -0,0 +1,259 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>An error occurred</title>
7
+ <link
8
+ rel="icon"
9
+ href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='45' fill='white' stroke='red' stroke-width='10'/%3E%3Cline x1='30' y1='30' x2='70' y2='70' stroke='red' stroke-width='10'/%3E%3Cline x1='70' y1='30' x2='30' y2='70' stroke='red' stroke-width='10'/%3E%3C/svg%3E"
10
+ type="image/svg+xml"
11
+ />
12
+ <style>
13
+ * {
14
+ padding: 0;
15
+ margin: 0;
16
+ box-sizing: border-box;
17
+ font-family: sans-serif;
18
+ }
19
+
20
+ body {
21
+ background: #f4f4f4;
22
+ }
23
+
24
+ .general-container {
25
+ display: flex;
26
+ }
27
+
28
+ .error-container {
29
+ position: absolute;
30
+ top: 40%;
31
+ left: 50%;
32
+ transform: translate(-50%, -50%);
33
+ padding: 1rem;
34
+ border-radius: 0.2rem;
35
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
36
+ background: #fff;
37
+ min-width: 300px;
38
+ }
39
+
40
+ .error h2 {
41
+ text-align: center;
42
+ }
43
+
44
+ .error-message {
45
+ margin-top: 2rem;
46
+ }
47
+
48
+ .error-message .general {
49
+ font-weight: bold;
50
+ font-size: 1rem;
51
+ color: #e74c3c;
52
+ }
53
+
54
+ .error-message .line {
55
+ font-size: 0.9rem;
56
+ }
57
+
58
+ .error-container {
59
+ border-left: 3px solid #e74c3c;
60
+ }
61
+
62
+ .error-container .content {
63
+ margin-top: 0.3rem;
64
+ }
65
+
66
+ .error-container .filename {
67
+ display: flex;
68
+ justify-content: flex-end;
69
+ }
70
+
71
+ .source-code-container {
72
+ display: flex;
73
+ flex-direction: column;
74
+ max-height: 400px;
75
+ overflow-y: auto;
76
+ }
77
+
78
+ .source-code-container span {
79
+ padding: 0.2rem;
80
+ font-family: system-ui;
81
+ font-size: 1rem;
82
+ }
83
+
84
+ .error-line-container {
85
+ display: flex;
86
+ }
87
+
88
+ .error-line-container .line-number {
89
+ background: #000;
90
+ color: #9b9b9b;
91
+ text-align: right;
92
+ min-width: 5ch;
93
+ padding-right: 0.5ch;
94
+ }
95
+
96
+ .error-line-container .error-line {
97
+ font-weight: 500;
98
+ }
99
+
100
+ .line-number.active {
101
+ color: #fff;
102
+ }
103
+
104
+ .error-line-container .html {
105
+ width: 100%;
106
+ }
107
+ .error-line {
108
+ background: #ffe9e9;
109
+ color: #c0392b;
110
+ }
111
+ .error-line-container:hover {
112
+ background-color: #f1f1f1;
113
+ }
114
+
115
+ .github {
116
+ display: flex;
117
+ justify-content: flex-end;
118
+ margin-top: 2rem;
119
+ }
120
+
121
+ .github button {
122
+ display: flex;
123
+ align-items: center;
124
+ gap: 0.5rem;
125
+ padding: 0.5rem;
126
+ cursor: pointer;
127
+ }
128
+
129
+ a {
130
+ text-decoration: none;
131
+ }
132
+
133
+ .github svg {
134
+ width: 20px;
135
+ }
136
+
137
+ .tips p {
138
+ font-size: 0.8rem;
139
+ margin-top: 0.1rem;
140
+ }
141
+
142
+ /* RESIZE */
143
+ .resize-handle {
144
+ top: 0;
145
+ position: absolute;
146
+ width: 6px;
147
+ height: 100%;
148
+ z-index: 2;
149
+ cursor: w-resize;
150
+ }
151
+ .resize-handle.left:hover,
152
+ .resize-handle.right:hover {
153
+ background-color: rgba(192, 192, 192, 0.479);
154
+ }
155
+ .resize-handle.left {
156
+ left: -3px;
157
+ border-radius: 0.5rem;
158
+ }
159
+ .resize-handle.right {
160
+ right: -3px;
161
+ border-radius: 0.5rem;
162
+ }
163
+ </style>
164
+ </head>
165
+
166
+ <body>
167
+ <div class="general-container">
168
+ <section class="error-container">
169
+ <div class="resize-handle left"></div>
170
+ <div class="resize-handle right"></div>
171
+ <div class="error">
172
+ <div class="header">
173
+ <h2>An error occurred</h2>
174
+ </div>
175
+
176
+ <div class="error-message">
177
+ <% const lineNumberMessage = lineNumber ? ` at line ${lineNumber} in ${filename}` : ""
178
+ %>
179
+ <p class="general"><%= `${message}${lineNumberMessage}` %></p>
180
+ <% if (isNaN(lineNumber)) { %>
181
+ <p class="line">⚠️ Error line number cannot be retrieved</p>
182
+ <% } %>
183
+ </div>
184
+
185
+ <div class="content">
186
+ <div class="source-code-container">
187
+ <% for (let i = 0; i < fileContent.length; i++) { %>
188
+
189
+ <div class="error-line-container">
190
+ <% const errorLine = i === lineNumber - 1 %>
191
+ <span class="<%= errorLine ? 'line-number active' : 'line-number' %>"
192
+ ><%= i + 1 %>
193
+ </span>
194
+ <span class="<%= errorLine ? 'html error-line' : 'html' %>">
195
+ <%= fileContent[i] %>
196
+ </span>
197
+ </div>
198
+ <% } %>
199
+ </div>
200
+ </div>
201
+ </div>
202
+ <div class="tips">
203
+ <p>Tip: resize the box by dragging its sides.</p>
204
+ </div>
205
+
206
+ <div class="github">
207
+ <a href="https://github.com/GreenFlag31/node-tao" target="_blank">
208
+ <button>
209
+ <span>GitHub</span>
210
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512">
211
+ <path
212
+ d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"
213
+ />
214
+ </svg>
215
+ </button>
216
+ </a>
217
+ </div>
218
+ </section>
219
+ </div>
220
+
221
+ <script>
222
+ const errorContainer = document.querySelector('.error-container');
223
+ const sourceCode = document.querySelector('.source-code-container');
224
+ const errorLine = sourceCode.querySelector('.error-line-container .line-number.active');
225
+ const resizeHandlers = document.querySelectorAll('.resize-handle');
226
+ const htmlLines = sourceCode.querySelectorAll('span.html');
227
+ const minWidth = errorContainer.clientWidth;
228
+ errorLine?.scrollIntoView();
229
+
230
+ resizeHandlers.forEach((resizer) => {
231
+ resizer.addEventListener('mousedown', (e) => {
232
+ e.preventDefault();
233
+ const startX = e.clientX;
234
+ const startWidth = errorContainer.offsetWidth;
235
+ const isLeft = resizer.classList.contains('left');
236
+
237
+ // 10 padding on each side
238
+ const maxWidth = window.innerWidth - 20;
239
+
240
+ const onMouseMove = (event) => {
241
+ const dx = event.clientX - startX;
242
+ const newWidth = isLeft ? startWidth - dx : startWidth + dx;
243
+ if (newWidth < minWidth || newWidth > maxWidth) return;
244
+
245
+ errorContainer.style.width = `${newWidth}px`;
246
+ };
247
+
248
+ const onMouseUp = () => {
249
+ document.removeEventListener('mousemove', onMouseMove);
250
+ document.removeEventListener('mouseup', onMouseUp);
251
+ };
252
+
253
+ document.addEventListener('mousemove', onMouseMove);
254
+ document.addEventListener('mouseup', onMouseUp);
255
+ });
256
+ });
257
+ </script>
258
+ </body>
259
+ </html>