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.
- package/README.md +230 -0
- package/checks.d.ts +8 -0
- package/checks.d.ts.map +1 -0
- package/checks.js +37 -0
- package/checks.js.map +1 -0
- package/const.d.ts +24 -0
- package/const.d.ts.map +1 -0
- package/const.js +51 -0
- package/const.js.map +1 -0
- package/default-config.d.ts +4 -0
- package/default-config.d.ts.map +1 -0
- package/default-config.js +25 -0
- package/default-config.js.map +1 -0
- package/error/error.html +259 -0
- package/error-utils.d.ts +21 -0
- package/error-utils.d.ts.map +1 -0
- package/error-utils.js +163 -0
- package/error-utils.js.map +1 -0
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -0
- package/index.js +6 -0
- package/index.js.map +1 -0
- package/init.d.ts +12 -0
- package/init.d.ts.map +1 -0
- package/init.js +31 -0
- package/init.js.map +1 -0
- package/interfaces.d.ts +170 -0
- package/interfaces.d.ts.map +1 -0
- package/interfaces.js +3 -0
- package/interfaces.js.map +1 -0
- package/metrics.d.ts +16 -0
- package/metrics.d.ts.map +1 -0
- package/metrics.js +80 -0
- package/metrics.js.map +1 -0
- package/package.json +45 -0
- package/parsing-helpers.d.ts +8 -0
- package/parsing-helpers.d.ts.map +1 -0
- package/parsing-helpers.js +60 -0
- package/parsing-helpers.js.map +1 -0
- package/store.d.ts +29 -0
- package/store.d.ts.map +1 -0
- package/store.js +52 -0
- package/store.js.map +1 -0
- package/tao.d.ts +81 -0
- package/tao.d.ts.map +1 -0
- package/tao.js +533 -0
- package/tao.js.map +1 -0
- package/templates-access.d.ts +15 -0
- package/templates-access.d.ts.map +1 -0
- package/templates-access.js +38 -0
- package/templates-access.js.map +1 -0
- package/utils.d.ts +45 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +227 -0
- 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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
package/checks.d.ts.map
ADDED
|
@@ -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
|
+
'&': '&',
|
|
45
|
+
'<': '<',
|
|
46
|
+
'>': '>',
|
|
47
|
+
'"': '"',
|
|
48
|
+
"'": ''',
|
|
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 @@
|
|
|
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"}
|
package/error/error.html
ADDED
|
@@ -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>
|