@itrocks/template 0.0.4 → 0.0.6
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 +11 -11
- package/cjs/template.js +777 -0
- package/package.json +13 -3
- package/template.d.ts +1 -0
- package/template.js +37 -40
package/README.md
CHANGED
@@ -372,13 +372,13 @@ the engine attempts to use the [Str](https://www.npmjs.com/package/@itrocks/rena
|
|
372
372
|
which provides string formatting functions. If no matching function is found, an error is thrown.
|
373
373
|
```ts
|
374
374
|
console.log(
|
375
|
-
new Template({ name: '
|
375
|
+
new Template({ name: 'EDITH' })
|
376
376
|
.parseBuffer('<span>{name.lcFirst}</span>')
|
377
377
|
)
|
378
378
|
```
|
379
379
|
Result:
|
380
380
|
```html
|
381
|
-
<span>
|
381
|
+
<span>eDITH</span>
|
382
382
|
```
|
383
383
|
|
384
384
|
### Including Another Template
|
@@ -467,10 +467,10 @@ are considered part of the phrase, so their text is also translated:
|
|
467
467
|
```ts
|
468
468
|
console.log(
|
469
469
|
new MyTemplate({ name: 'Nick' })
|
470
|
-
.parseBuffer(`
|
471
|
-
|
472
|
-
|
473
|
-
|
470
|
+
.parseBuffer(`
|
471
|
+
<h2>What is my name</h2>
|
472
|
+
<p>My <span>name</span> is {name}</p>
|
473
|
+
`)
|
474
474
|
)
|
475
475
|
```
|
476
476
|
Results in:
|
@@ -530,11 +530,11 @@ fetches data from an external API or applies a custom transformation.
|
|
530
530
|
|
531
531
|
```ts
|
532
532
|
const myParser: VariableParser = [
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
533
|
+
'@',
|
534
|
+
(variable, data) => {
|
535
|
+
const key = variable.substring(1) // remove '@'
|
536
|
+
return fetchFromCustomDataSource(key)
|
537
|
+
}
|
538
538
|
]
|
539
539
|
template.parsers.push(myParser)
|
540
540
|
```
|
package/cjs/template.js
ADDED
@@ -0,0 +1,777 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.Template = exports.frontScripts = void 0;
|
4
|
+
const rename_1 = require("@itrocks/rename");
|
5
|
+
const app_dir_1 = require("@itrocks/app-dir");
|
6
|
+
const sorted_array_1 = require("@itrocks/sorted-array");
|
7
|
+
const promises_1 = require("node:fs/promises");
|
8
|
+
const node_path_1 = require("node:path");
|
9
|
+
let blockBack;
|
10
|
+
let blockStack;
|
11
|
+
let doHeadLinks = false;
|
12
|
+
let index;
|
13
|
+
let length;
|
14
|
+
let source;
|
15
|
+
let start;
|
16
|
+
let tagName;
|
17
|
+
let tagStack;
|
18
|
+
let target;
|
19
|
+
let targetStack;
|
20
|
+
let lockLiteral;
|
21
|
+
let literalPartStack;
|
22
|
+
let literalParts;
|
23
|
+
let inLiteral;
|
24
|
+
exports.frontScripts = new sorted_array_1.SortedArray();
|
25
|
+
exports.frontScripts.distinct = true;
|
26
|
+
let doneLinks = new sorted_array_1.SortedArray();
|
27
|
+
let headLinks = new sorted_array_1.SortedArray();
|
28
|
+
let headTitle = undefined;
|
29
|
+
doneLinks.distinct = true;
|
30
|
+
headLinks.distinct = true;
|
31
|
+
class Template {
|
32
|
+
data;
|
33
|
+
containerData;
|
34
|
+
doExpression = true;
|
35
|
+
doLiteral = false;
|
36
|
+
fileName;
|
37
|
+
filePath;
|
38
|
+
included = false;
|
39
|
+
// Inline elements are replaced by $1 when in literal.
|
40
|
+
inlineElements = new sorted_array_1.SortedArray('a', 'abbr', 'acronym', 'b', 'bdo', 'big', 'button', 'cite', 'code', 'data', 'del', 'dfn', 'em', 'font', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'mark', 'meter', 'object', 'optgroup', 'option', 'output', 'picture', 'q', 'rt', 'samp', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'svg', 'time', 'tspan', 'tt', 'u', 'var', 'wbr');
|
41
|
+
// These attribute values are literals.
|
42
|
+
literalAttributes = new sorted_array_1.SortedArray('alt', 'enterkeyhint', 'label', 'lang', 'placeholder', 'srcdoc', 'title');
|
43
|
+
// These element contents are literals.
|
44
|
+
literalElements = new sorted_array_1.SortedArray('a', 'abbr', 'acronym', 'article', 'aside', 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'caption', 'center', 'cite', 'data', 'datalist', 'dd', 'del', 'desc', 'details', 'dfn', 'dialog', 'div', 'dt', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hr', 'i', 'iframe', 'ins', 'keygen', 'label', 'legend', 'li', 'main', 'mark', 'menuitem', 'meter', 'nav', 'noframes', 'noscript', 'optgroup', 'option', 'p', 'pre', 'q', 'rb', 's', 'section', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'summary', 'sup', 'td', 'template', 'text', 'textarea', 'textpath', 'th', 'time', 'title', 'tspan', 'u', 'wbr');
|
45
|
+
onAttribute;
|
46
|
+
onTagOpen;
|
47
|
+
onTagOpened;
|
48
|
+
onTagClose;
|
49
|
+
parsers = [];
|
50
|
+
prefixes;
|
51
|
+
// These elements have no closing tag.
|
52
|
+
unclosingTags = new sorted_array_1.SortedArray('area', 'base', 'basefont', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track');
|
53
|
+
constructor(data, containerData) {
|
54
|
+
this.data = data;
|
55
|
+
this.containerData = containerData;
|
56
|
+
blockStack = [];
|
57
|
+
if (containerData) {
|
58
|
+
blockStack.push({ blockStart: 0, collection: [], data: containerData, iteration: 0, iterations: 1 });
|
59
|
+
}
|
60
|
+
this.prefixes = this.parsers.map(([prefix]) => prefix).join('');
|
61
|
+
}
|
62
|
+
applyLiterals(text, parts = []) {
|
63
|
+
return text.replace(/\$([0-9]+)/g, (_, index) => parts[+index]);
|
64
|
+
}
|
65
|
+
closeTag(shouldInLiteral, targetIndex) {
|
66
|
+
shouldInLiteral ||= inLiteral;
|
67
|
+
({ tagName, inLiteral } = tagStack.pop() ?? { tagName: '', inLiteral: false });
|
68
|
+
if (this.onTagClose)
|
69
|
+
this.onTagClose.call(this, tagName);
|
70
|
+
if ((tagName[0] === 'a') && (tagName === 'address')) {
|
71
|
+
lockLiteral = false;
|
72
|
+
}
|
73
|
+
if (inLiteral && this.inlineElements.includes(tagName)) {
|
74
|
+
if (this.literalElements.includes(tagName)) {
|
75
|
+
this.literalTarget(targetIndex);
|
76
|
+
}
|
77
|
+
literalParts = literalPartStack.pop();
|
78
|
+
literalParts.push(target + source.substring(start, index));
|
79
|
+
start = index;
|
80
|
+
target = targetStack.pop() + '$' + literalParts.length;
|
81
|
+
shouldInLiteral = false;
|
82
|
+
}
|
83
|
+
return shouldInLiteral;
|
84
|
+
}
|
85
|
+
combineLiterals(text, parts) {
|
86
|
+
const original = text;
|
87
|
+
text = text.trimEnd();
|
88
|
+
const right = text.length;
|
89
|
+
let left = text.length;
|
90
|
+
text = text.trimStart();
|
91
|
+
left -= text.length;
|
92
|
+
if (text !== '') {
|
93
|
+
text = (parts && /^(\$[1-9][0-9]*)+$/.test(text))
|
94
|
+
? parts.join('')
|
95
|
+
: this.applyLiterals(text, parts?.map(part => ((typeof part)[0] === 's') ? this.applyLiterals(part) : part));
|
96
|
+
}
|
97
|
+
return original.substring(0, left) + text + original.substring(right);
|
98
|
+
}
|
99
|
+
debugEvents() {
|
100
|
+
this.onAttribute = (name, value) => console.log('attribute', name, '=', value);
|
101
|
+
this.onTagOpen = (name) => console.log('tag.open =', name);
|
102
|
+
this.onTagOpened = (name) => console.log('tag.opened =', name);
|
103
|
+
this.onTagClose = (name) => console.log('tag.closed =', name);
|
104
|
+
}
|
105
|
+
getCleanContext() {
|
106
|
+
const doneLinks = new sorted_array_1.SortedArray;
|
107
|
+
const headLinks = new sorted_array_1.SortedArray;
|
108
|
+
doneLinks.distinct = true;
|
109
|
+
headLinks.distinct = true;
|
110
|
+
return {
|
111
|
+
doHeadLinks: false,
|
112
|
+
doneLinks: doneLinks,
|
113
|
+
headLinks: headLinks,
|
114
|
+
index: length,
|
115
|
+
length: source.length,
|
116
|
+
source: source,
|
117
|
+
start: length,
|
118
|
+
target: target,
|
119
|
+
targetStack: [],
|
120
|
+
literalPartStack: [],
|
121
|
+
literalParts: [],
|
122
|
+
inLiteral: this.doLiteral
|
123
|
+
};
|
124
|
+
}
|
125
|
+
getPosition() {
|
126
|
+
return { index, start, target };
|
127
|
+
}
|
128
|
+
getContext() {
|
129
|
+
return {
|
130
|
+
doHeadLinks, doneLinks, headLinks, index, length, source, start, target, targetStack,
|
131
|
+
literalPartStack, literalParts, inLiteral
|
132
|
+
};
|
133
|
+
}
|
134
|
+
async include(path, data) {
|
135
|
+
const back = {
|
136
|
+
doHeadLinks, index, length, source, start, tagName, tagStack, target, targetStack,
|
137
|
+
literalParts, literalPartStack, inLiteral, lockLiteral
|
138
|
+
};
|
139
|
+
doHeadLinks = true;
|
140
|
+
const template = new (Object.getPrototypeOf(this).constructor)(data, blockStack[0]?.data);
|
141
|
+
template.included = true;
|
142
|
+
template.doExpression = this.doExpression;
|
143
|
+
template.doLiteral = this.doLiteral;
|
144
|
+
template.onAttribute = this.onAttribute;
|
145
|
+
template.onTagClose = this.onTagClose;
|
146
|
+
template.onTagOpen = this.onTagOpen;
|
147
|
+
template.onTagOpened = this.onTagOpened;
|
148
|
+
template.parsers = this.parsers;
|
149
|
+
const parsed = await template.parseFile(((path[0] === node_path_1.sep) || (path[1] === ':')) ? path : (this.filePath + node_path_1.sep + path));
|
150
|
+
({
|
151
|
+
doHeadLinks, index, length, source, start, tagName, tagStack, target, targetStack,
|
152
|
+
literalParts, literalPartStack, inLiteral, lockLiteral
|
153
|
+
} = back);
|
154
|
+
return parsed.substring(parsed.indexOf('<!--BEGIN-->') + 12, parsed.indexOf('<!--END-->'));
|
155
|
+
}
|
156
|
+
isContextClean() {
|
157
|
+
const clean = this.getCleanContext();
|
158
|
+
const context = this.getContext();
|
159
|
+
return context.doHeadLinks === clean.doHeadLinks
|
160
|
+
&& context.doneLinks.distinct === clean.doneLinks.distinct
|
161
|
+
&& context.doneLinks.length === clean.doneLinks.length
|
162
|
+
&& context.headLinks.distinct === clean.headLinks.distinct
|
163
|
+
&& context.headLinks.length === clean.headLinks.length
|
164
|
+
&& context.index === clean.index
|
165
|
+
&& context.length === clean.length
|
166
|
+
&& context.start === clean.start
|
167
|
+
&& context.targetStack.length === clean.targetStack.length
|
168
|
+
&& context.literalPartStack.length === clean.literalPartStack.length
|
169
|
+
&& context.literalParts.length === clean.literalParts.length
|
170
|
+
&& context.inLiteral === clean.inLiteral;
|
171
|
+
}
|
172
|
+
literalTarget(index, isTitle = false) {
|
173
|
+
let combined;
|
174
|
+
if (literalParts.length) {
|
175
|
+
target += source.substring(start, index);
|
176
|
+
combined = this.combineLiterals(target, literalParts);
|
177
|
+
target = (targetStack.pop() ?? '') + combined;
|
178
|
+
literalParts = [];
|
179
|
+
}
|
180
|
+
else {
|
181
|
+
combined = this.combineLiterals(source.substring(start, index));
|
182
|
+
target += combined;
|
183
|
+
}
|
184
|
+
if (isTitle && doHeadLinks) {
|
185
|
+
headTitle = combined;
|
186
|
+
}
|
187
|
+
start = index;
|
188
|
+
}
|
189
|
+
async parseBuffer(buffer) {
|
190
|
+
this.setSource(buffer);
|
191
|
+
await this.parseVars();
|
192
|
+
if (doHeadLinks) {
|
193
|
+
return target;
|
194
|
+
}
|
195
|
+
if (headLinks.length) {
|
196
|
+
const position = target.lastIndexOf('>', target.indexOf('</head>')) + 1;
|
197
|
+
target = target.slice(0, position) + '\n\t' + headLinks.join('\n\t') + target.slice(position);
|
198
|
+
doneLinks = new sorted_array_1.SortedArray;
|
199
|
+
doneLinks.distinct = true;
|
200
|
+
headLinks = new sorted_array_1.SortedArray;
|
201
|
+
headLinks.distinct = true;
|
202
|
+
}
|
203
|
+
if (headTitle && !this.included) {
|
204
|
+
const position = target.indexOf('>', target.indexOf('<title') + 6) + 1;
|
205
|
+
target = target.slice(0, position) + headTitle + target.slice(target.indexOf('</title>', position));
|
206
|
+
}
|
207
|
+
return target;
|
208
|
+
}
|
209
|
+
async parseExpression(data, close, finalClose = '') {
|
210
|
+
const indexOut = index;
|
211
|
+
let open = source[index];
|
212
|
+
if (inLiteral && !literalParts.length) {
|
213
|
+
targetStack.push(target);
|
214
|
+
target = '';
|
215
|
+
}
|
216
|
+
if (open === '<') {
|
217
|
+
index += 3;
|
218
|
+
open = '{';
|
219
|
+
}
|
220
|
+
index++;
|
221
|
+
const firstChar = source[index];
|
222
|
+
if ((index >= length) || !this.startsExpression(firstChar, open, close)) {
|
223
|
+
return;
|
224
|
+
}
|
225
|
+
let conditional = (firstChar === '?');
|
226
|
+
const finalChar = finalClose.length ? finalClose[0] : '';
|
227
|
+
let stackPos = targetStack.length;
|
228
|
+
if (conditional) {
|
229
|
+
index++;
|
230
|
+
}
|
231
|
+
targetStack.push(target + source.substring(start, indexOut));
|
232
|
+
start = index;
|
233
|
+
target = '';
|
234
|
+
while (index < length) {
|
235
|
+
const char = source[index];
|
236
|
+
if (char === open) {
|
237
|
+
targetStack.push(target + source.substring(start, index));
|
238
|
+
index++;
|
239
|
+
start = index;
|
240
|
+
target = '';
|
241
|
+
continue;
|
242
|
+
}
|
243
|
+
if ((char === close)
|
244
|
+
|| ((char === finalChar) && (source.substring(index, index + finalClose.length) === finalClose))) {
|
245
|
+
let minus = 0;
|
246
|
+
if (source[index - 1] === '?') {
|
247
|
+
conditional = true;
|
248
|
+
minus = 1;
|
249
|
+
}
|
250
|
+
const expression = target + source.substring(start, index - minus);
|
251
|
+
const lastTarget = targetStack.pop();
|
252
|
+
const parsed = await this.parsePath(expression, data);
|
253
|
+
index += (char === close) ? 1 : finalClose.length;
|
254
|
+
start = index;
|
255
|
+
target = '';
|
256
|
+
if (char === finalChar)
|
257
|
+
while (targetStack.length > stackPos) {
|
258
|
+
target += targetStack.shift();
|
259
|
+
}
|
260
|
+
if (inLiteral && (targetStack.length === stackPos)) {
|
261
|
+
literalParts.push(parsed);
|
262
|
+
target += lastTarget + '$' + literalParts.length;
|
263
|
+
return conditional;
|
264
|
+
}
|
265
|
+
if (lastTarget.length || target.length) {
|
266
|
+
target += lastTarget + parsed;
|
267
|
+
}
|
268
|
+
else {
|
269
|
+
target = parsed;
|
270
|
+
}
|
271
|
+
if (targetStack.length === stackPos) {
|
272
|
+
if (conditional && !parsed) {
|
273
|
+
if ((typeof target)[0] === 's') {
|
274
|
+
target = target.substring(0, target.lastIndexOf(' '));
|
275
|
+
while ((index < length) && !' \n\r\t\f'.includes(source[index])) {
|
276
|
+
index++;
|
277
|
+
start++;
|
278
|
+
}
|
279
|
+
index--;
|
280
|
+
}
|
281
|
+
return conditional;
|
282
|
+
}
|
283
|
+
return conditional;
|
284
|
+
}
|
285
|
+
continue;
|
286
|
+
}
|
287
|
+
if ((char === '"') || (char === "'")) {
|
288
|
+
index++;
|
289
|
+
let c;
|
290
|
+
while ((index < length) && ((c = source[index]) !== char)) {
|
291
|
+
if (c === '\\')
|
292
|
+
index++;
|
293
|
+
index++;
|
294
|
+
}
|
295
|
+
}
|
296
|
+
index++;
|
297
|
+
}
|
298
|
+
// bad close
|
299
|
+
stackPos++;
|
300
|
+
while (targetStack.length > stackPos) {
|
301
|
+
target = targetStack.pop() + open + target;
|
302
|
+
}
|
303
|
+
target = targetStack.pop() + (finalClose.length ? '<!--' : open) + target;
|
304
|
+
return conditional;
|
305
|
+
}
|
306
|
+
async parseFile(fileName, containerFileName) {
|
307
|
+
if (containerFileName && !this.included) {
|
308
|
+
const data = this.data;
|
309
|
+
this.data = Object.assign({ content: () => this.include(fileName, data) }, blockStack[0]?.data);
|
310
|
+
return this.parseFile((0, node_path_1.normalize)(containerFileName));
|
311
|
+
}
|
312
|
+
this.fileName = fileName.substring(fileName.lastIndexOf(node_path_1.sep) + 1);
|
313
|
+
this.filePath = fileName.substring(0, fileName.lastIndexOf(node_path_1.sep));
|
314
|
+
return this.parseBuffer(await (0, promises_1.readFile)(fileName, 'utf-8'));
|
315
|
+
}
|
316
|
+
async parsePath(expression, data) {
|
317
|
+
if (expression === '') {
|
318
|
+
return undefined;
|
319
|
+
}
|
320
|
+
if ((expression[0] === '.') && (expression.startsWith('./') || expression.startsWith('../'))) {
|
321
|
+
return this.include(expression, data);
|
322
|
+
}
|
323
|
+
blockBack = 0;
|
324
|
+
for (const variable of expression.split('.')) {
|
325
|
+
data = await this.parseVariable(variable, data);
|
326
|
+
}
|
327
|
+
return data;
|
328
|
+
}
|
329
|
+
async parseVariable(variable, data) {
|
330
|
+
if (variable === '') {
|
331
|
+
return (typeof data === 'function')
|
332
|
+
? data.call()
|
333
|
+
: data;
|
334
|
+
}
|
335
|
+
if (variable === '*') {
|
336
|
+
return (typeof data === 'object') ? Object.values(data) : data;
|
337
|
+
}
|
338
|
+
const firstChar = variable[0];
|
339
|
+
if ((firstChar === 'B') && (variable === 'BEGIN')) {
|
340
|
+
return data;
|
341
|
+
}
|
342
|
+
if (((firstChar === '"') && (variable[variable.length - 1] === '"'))
|
343
|
+
|| ((firstChar === "'") && (variable[variable.length - 1] === "'"))) {
|
344
|
+
return variable.substring(1, variable.length - 1);
|
345
|
+
}
|
346
|
+
if (firstChar === '-') {
|
347
|
+
blockBack++;
|
348
|
+
return blockStack[blockStack.length - blockBack].data;
|
349
|
+
}
|
350
|
+
for (const [prefix, callback] of this.parsers) {
|
351
|
+
if (firstChar === prefix) {
|
352
|
+
return callback(variable, data);
|
353
|
+
}
|
354
|
+
}
|
355
|
+
if (data[variable] === undefined) {
|
356
|
+
data = new rename_1.default(data);
|
357
|
+
}
|
358
|
+
let value = data[variable];
|
359
|
+
return ((typeof value === 'function') && !value.prototype)
|
360
|
+
? value.call(data)
|
361
|
+
: value;
|
362
|
+
}
|
363
|
+
async parseVars() {
|
364
|
+
let blockStart = 0;
|
365
|
+
let collection = [];
|
366
|
+
let data = this.data;
|
367
|
+
let inHead = false;
|
368
|
+
let iteration = 0;
|
369
|
+
let iterations = 0;
|
370
|
+
while (index < length) {
|
371
|
+
let char = source[index];
|
372
|
+
// expression
|
373
|
+
if ((char === '{') && this.doExpression) {
|
374
|
+
await this.parseExpression(data, '}');
|
375
|
+
continue;
|
376
|
+
}
|
377
|
+
// tag ?
|
378
|
+
if (char !== '<') {
|
379
|
+
index++;
|
380
|
+
continue;
|
381
|
+
}
|
382
|
+
const tagIndex = index;
|
383
|
+
char = source[++index];
|
384
|
+
if (char === '!') {
|
385
|
+
if (inLiteral) {
|
386
|
+
this.literalTarget(tagIndex);
|
387
|
+
}
|
388
|
+
char = source[++index];
|
389
|
+
index++;
|
390
|
+
// comment tag
|
391
|
+
if ((char === '-') && (source[index] === '-')) {
|
392
|
+
index++;
|
393
|
+
if (!/[a-z0-9@%{]/i.test(source[index])
|
394
|
+
|| !this.doExpression
|
395
|
+
|| ((source[index] === 'B') && this.included && (source.substring(index, index + 8) === 'BEGIN-->'))
|
396
|
+
|| ((source[index] === 'E') && this.included && (source.substring(index, index + 6) === 'END-->'))) {
|
397
|
+
index = source.indexOf('-->', index) + 3;
|
398
|
+
if (index === 2)
|
399
|
+
break;
|
400
|
+
if (inLiteral && (index > start)) {
|
401
|
+
this.sourceToTarget();
|
402
|
+
}
|
403
|
+
continue;
|
404
|
+
}
|
405
|
+
// end condition / loop block
|
406
|
+
if ('eE'.includes(source[index]) && ['end-->', 'END-->'].includes(source.substring(index, index + 6))) {
|
407
|
+
target += this.trimEndLine(source.substring(start, tagIndex));
|
408
|
+
iteration++;
|
409
|
+
if (iteration < iterations) {
|
410
|
+
data = collection[iteration];
|
411
|
+
index = start = blockStart;
|
412
|
+
if (inLiteral && (index > start)) {
|
413
|
+
this.sourceToTarget();
|
414
|
+
}
|
415
|
+
continue;
|
416
|
+
}
|
417
|
+
({ blockStart, collection, data, iteration, iterations } = blockStack.pop()
|
418
|
+
?? { blockStart: 0, collection: [], data: undefined, iteration: 0, iterations: 0 });
|
419
|
+
index += 6;
|
420
|
+
start = index;
|
421
|
+
if (inLiteral && (index > start)) {
|
422
|
+
this.sourceToTarget();
|
423
|
+
}
|
424
|
+
continue;
|
425
|
+
}
|
426
|
+
// begin condition / loop block
|
427
|
+
blockStack.push({ blockStart, collection, data, iteration, iterations });
|
428
|
+
if (tagIndex > start) {
|
429
|
+
target += this.trimEndLine(source.substring(start, tagIndex));
|
430
|
+
start = tagIndex;
|
431
|
+
}
|
432
|
+
const backTarget = target;
|
433
|
+
const backInLiteral = inLiteral;
|
434
|
+
index = tagIndex;
|
435
|
+
target = '';
|
436
|
+
inLiteral = false;
|
437
|
+
const condition = await this.parseExpression(data, '}', '-->');
|
438
|
+
let blockData = condition ? (target ? data : undefined) : target;
|
439
|
+
blockStart = index;
|
440
|
+
iteration = 0;
|
441
|
+
target = backTarget;
|
442
|
+
inLiteral = backInLiteral;
|
443
|
+
if (Array.isArray(blockData)) {
|
444
|
+
collection = blockData;
|
445
|
+
data = collection[0];
|
446
|
+
iterations = collection.length;
|
447
|
+
}
|
448
|
+
else {
|
449
|
+
collection = [];
|
450
|
+
data = blockData;
|
451
|
+
iterations = data ? 1 : 0;
|
452
|
+
}
|
453
|
+
if (!iterations) {
|
454
|
+
this.skipBlock();
|
455
|
+
continue;
|
456
|
+
}
|
457
|
+
if (inLiteral && (index > start)) {
|
458
|
+
this.sourceToTarget();
|
459
|
+
}
|
460
|
+
continue;
|
461
|
+
}
|
462
|
+
// cdata section
|
463
|
+
if ((char === '[') && (source.substring(index, index + 6) === 'CDATA[')) {
|
464
|
+
index = source.indexOf(']]>', index + 6) + 3;
|
465
|
+
if (index === 2)
|
466
|
+
break;
|
467
|
+
}
|
468
|
+
// DOCTYPE
|
469
|
+
else {
|
470
|
+
index = source.indexOf('>', index) + 1;
|
471
|
+
}
|
472
|
+
if (inLiteral) {
|
473
|
+
this.sourceToTarget();
|
474
|
+
}
|
475
|
+
continue;
|
476
|
+
}
|
477
|
+
// tag close
|
478
|
+
if (char === '/') {
|
479
|
+
index++;
|
480
|
+
const closeTagName = source.substring(index, source.indexOf('>', index));
|
481
|
+
index += closeTagName.length + 1;
|
482
|
+
if (inHead && (closeTagName[0] === 'h') && (closeTagName === 'head')) {
|
483
|
+
inHead = false;
|
484
|
+
if (!doHeadLinks) {
|
485
|
+
doneLinks = headLinks;
|
486
|
+
headLinks = new sorted_array_1.SortedArray;
|
487
|
+
headLinks.distinct = true;
|
488
|
+
}
|
489
|
+
}
|
490
|
+
let shouldInLiteral = inLiteral;
|
491
|
+
if (!this.unclosingTags.includes(closeTagName)) {
|
492
|
+
do {
|
493
|
+
shouldInLiteral = this.closeTag(shouldInLiteral, tagIndex);
|
494
|
+
} while ((tagName !== closeTagName) && tagName.length);
|
495
|
+
}
|
496
|
+
if (shouldInLiteral) {
|
497
|
+
lockLiteral = false;
|
498
|
+
this.literalTarget(tagIndex, (tagName[0] === 't') && (tagName === 'title'));
|
499
|
+
}
|
500
|
+
if (inLiteral && (index > start)) {
|
501
|
+
this.sourceToTarget();
|
502
|
+
}
|
503
|
+
continue;
|
504
|
+
}
|
505
|
+
// tag open
|
506
|
+
while ((index < length) && !' >\n\r\t\f'.includes(source[index]))
|
507
|
+
index++;
|
508
|
+
tagName = source.substring(tagIndex + 1, index);
|
509
|
+
if (this.onTagOpen)
|
510
|
+
this.onTagOpen.call(this, tagName);
|
511
|
+
while (' \n\r\t\f'.includes(source[index]))
|
512
|
+
index++;
|
513
|
+
char = tagName[0];
|
514
|
+
if ((char === 'h') && (tagName === 'head')) {
|
515
|
+
inHead = true;
|
516
|
+
}
|
517
|
+
const unclosingTag = this.unclosingTags.includes(tagName);
|
518
|
+
if (!unclosingTag) {
|
519
|
+
tagStack.push({ tagName, inLiteral });
|
520
|
+
}
|
521
|
+
let inlineElement = false;
|
522
|
+
let pushedParts = false;
|
523
|
+
if (inLiteral) {
|
524
|
+
inlineElement = this.inlineElements.includes(tagName);
|
525
|
+
if (inlineElement) {
|
526
|
+
if (literalParts.length) {
|
527
|
+
targetStack.push(target + source.substring(start, tagIndex));
|
528
|
+
}
|
529
|
+
else {
|
530
|
+
targetStack.push(target, source.substring(start, tagIndex));
|
531
|
+
}
|
532
|
+
start = tagIndex;
|
533
|
+
target = '';
|
534
|
+
if (!unclosingTag) {
|
535
|
+
literalPartStack.push(literalParts);
|
536
|
+
literalParts = [];
|
537
|
+
pushedParts = true;
|
538
|
+
}
|
539
|
+
}
|
540
|
+
else {
|
541
|
+
this.literalTarget(tagIndex);
|
542
|
+
}
|
543
|
+
}
|
544
|
+
const elementInLiteral = inLiteral;
|
545
|
+
// attributes
|
546
|
+
let hasTypeSubmit = false;
|
547
|
+
const inInput = (char === 'i') && (tagName === 'input');
|
548
|
+
const inLink = (char === 'l') && (tagName === 'link');
|
549
|
+
const inScript = (char === 's') && (tagName === 'script');
|
550
|
+
let targetTagIndex = -1;
|
551
|
+
if (inHead && (inLink || inScript)) {
|
552
|
+
this.sourceToTarget();
|
553
|
+
targetTagIndex = target.lastIndexOf('<');
|
554
|
+
}
|
555
|
+
while (source[index] !== '>') {
|
556
|
+
// attribute name
|
557
|
+
const position = index;
|
558
|
+
while ((index < length) && !' =>\n\r\t\f'.includes(source[index]))
|
559
|
+
index++;
|
560
|
+
const attributeName = source.substring(position, index);
|
561
|
+
while (' \n\r\t\f'.includes(source[index]))
|
562
|
+
index++;
|
563
|
+
// attribute value
|
564
|
+
if (source[index] === '=') {
|
565
|
+
index++;
|
566
|
+
while (' \n\r\t\f'.includes(source[index]))
|
567
|
+
index++;
|
568
|
+
const attributeChar = attributeName[0];
|
569
|
+
const [open, close] = ('afhls'.includes(attributeChar)
|
570
|
+
&& ['action', 'formaction', 'href', 'location', 'src'].includes(attributeName)) ? ['(', ')']
|
571
|
+
: ['{', '}'];
|
572
|
+
let quote = source[index];
|
573
|
+
if ((quote === '"') || (quote === "'")) {
|
574
|
+
index++;
|
575
|
+
}
|
576
|
+
else {
|
577
|
+
quote = ' >';
|
578
|
+
}
|
579
|
+
if ((open === '(') && (source.substring(index, index + 6) === 'app://')) {
|
580
|
+
this.sourceToTarget();
|
581
|
+
index += 6;
|
582
|
+
start = index;
|
583
|
+
}
|
584
|
+
inLiteral = this.doLiteral && (this.literalAttributes.includes(attributeName)
|
585
|
+
|| (hasTypeSubmit && (attributeChar === 'v') && (attributeName === 'value')));
|
586
|
+
if (inLiteral && !pushedParts && unclosingTag && literalParts.length) {
|
587
|
+
literalPartStack.push(literalParts);
|
588
|
+
literalParts = [];
|
589
|
+
pushedParts = true;
|
590
|
+
}
|
591
|
+
const inLinkHRef = inLink && (attributeChar === 'h') && (attributeName === 'href');
|
592
|
+
const inScriptSrc = inScript && (attributeChar === 's') && (attributeName === 'src');
|
593
|
+
if ((inLinkHRef || inScriptSrc || inLiteral) && (index > start)) {
|
594
|
+
this.sourceToTarget();
|
595
|
+
}
|
596
|
+
const position = index;
|
597
|
+
const shortQuote = !(quote.length - 1);
|
598
|
+
while (index < length) {
|
599
|
+
const char = source[index];
|
600
|
+
// end of attribute value
|
601
|
+
if (shortQuote ? (char === quote) : quote.includes(char)) {
|
602
|
+
const attributeValue = source.substring(position, index);
|
603
|
+
if (inInput) {
|
604
|
+
hasTypeSubmit ||= ((attributeChar === 't') && (attributeValue[0] === 's')
|
605
|
+
&& (attributeName === 'type') && (attributeValue === 'submit'));
|
606
|
+
}
|
607
|
+
if (inLiteral) {
|
608
|
+
this.literalTarget(index);
|
609
|
+
}
|
610
|
+
if (inLinkHRef && attributeValue.endsWith('.css')) {
|
611
|
+
let frontStyle = (0, node_path_1.normalize)(this.filePath + node_path_1.sep + source.substring(start, index))
|
612
|
+
.substring(app_dir_1.default.length);
|
613
|
+
if (node_path_1.sep !== '/') {
|
614
|
+
frontStyle = frontStyle.replaceAll(node_path_1.sep, '/');
|
615
|
+
}
|
616
|
+
target += frontStyle;
|
617
|
+
start = index;
|
618
|
+
}
|
619
|
+
if (inScriptSrc && attributeValue.endsWith('.js')) {
|
620
|
+
let frontScript = (0, node_path_1.normalize)(this.filePath + node_path_1.sep + source.substring(start, index))
|
621
|
+
.substring(app_dir_1.default.length);
|
622
|
+
if (node_path_1.sep !== '/') {
|
623
|
+
frontScript = frontScript.replaceAll(node_path_1.sep, '/');
|
624
|
+
}
|
625
|
+
exports.frontScripts.insert(frontScript);
|
626
|
+
target += frontScript;
|
627
|
+
start = index;
|
628
|
+
}
|
629
|
+
if (this.onAttribute)
|
630
|
+
this.onAttribute(attributeName, attributeValue);
|
631
|
+
if (char !== '>')
|
632
|
+
index++;
|
633
|
+
break;
|
634
|
+
}
|
635
|
+
// expression in attribute value
|
636
|
+
if ((char === open) && this.doExpression) {
|
637
|
+
await this.parseExpression(data, close);
|
638
|
+
continue;
|
639
|
+
}
|
640
|
+
index++;
|
641
|
+
}
|
642
|
+
}
|
643
|
+
else if (this.onAttribute)
|
644
|
+
this.onAttribute(attributeName, '');
|
645
|
+
// next attribute
|
646
|
+
while (' \n\r\t\f'.includes(source[index]))
|
647
|
+
index++;
|
648
|
+
}
|
649
|
+
index++;
|
650
|
+
if (this.onTagOpened)
|
651
|
+
this.onTagOpened.call(this, tagName);
|
652
|
+
// skip script content
|
653
|
+
if (inScript) {
|
654
|
+
if (this.onTagClose)
|
655
|
+
this.onTagClose.call(this, 'script');
|
656
|
+
index = source.indexOf('</script>', index) + 9;
|
657
|
+
if (index === 8)
|
658
|
+
break;
|
659
|
+
if (inLiteral && (index > start)) {
|
660
|
+
this.sourceToTarget();
|
661
|
+
}
|
662
|
+
}
|
663
|
+
if (targetTagIndex > -1) {
|
664
|
+
this.sourceToTarget();
|
665
|
+
const headLink = target.substring(targetTagIndex);
|
666
|
+
if (!doneLinks || !doneLinks.includes(headLink)) {
|
667
|
+
headLinks.insert(headLink);
|
668
|
+
}
|
669
|
+
}
|
670
|
+
if (inScript) {
|
671
|
+
continue;
|
672
|
+
}
|
673
|
+
if (unclosingTag) {
|
674
|
+
if (pushedParts) {
|
675
|
+
literalParts = literalPartStack.pop();
|
676
|
+
}
|
677
|
+
inLiteral = elementInLiteral;
|
678
|
+
if (this.onTagClose)
|
679
|
+
this.onTagClose.call(this, tagName);
|
680
|
+
if (inLiteral) {
|
681
|
+
if (index > start) {
|
682
|
+
this.sourceToTarget();
|
683
|
+
}
|
684
|
+
if (inlineElement) {
|
685
|
+
literalParts.push(target);
|
686
|
+
target = targetStack.pop() + '$' + literalParts.length;
|
687
|
+
}
|
688
|
+
}
|
689
|
+
}
|
690
|
+
else {
|
691
|
+
lockLiteral ||= (tagName[0] === 'a') && (tagName === 'address');
|
692
|
+
inLiteral = this.doLiteral && !lockLiteral && this.literalElements.includes(tagName);
|
693
|
+
if (inLiteral && (index > start)) {
|
694
|
+
this.sourceToTarget();
|
695
|
+
}
|
696
|
+
}
|
697
|
+
}
|
698
|
+
if (tagStack.length) {
|
699
|
+
let shouldInLiteral = inLiteral;
|
700
|
+
while (tagStack.length) {
|
701
|
+
shouldInLiteral = this.closeTag(shouldInLiteral, length);
|
702
|
+
}
|
703
|
+
if (shouldInLiteral) {
|
704
|
+
this.literalTarget(length);
|
705
|
+
}
|
706
|
+
return target;
|
707
|
+
}
|
708
|
+
if (inLiteral) {
|
709
|
+
this.literalTarget(index);
|
710
|
+
}
|
711
|
+
if (start < length) {
|
712
|
+
target += source.substring(start);
|
713
|
+
start = length;
|
714
|
+
}
|
715
|
+
return target;
|
716
|
+
}
|
717
|
+
setSource(setSource, setIndex = 0, setStart, setTarget = '') {
|
718
|
+
index = setIndex;
|
719
|
+
length = setSource.length;
|
720
|
+
source = setSource;
|
721
|
+
start = setStart ?? index;
|
722
|
+
tagName = '';
|
723
|
+
tagStack = [];
|
724
|
+
target = setTarget;
|
725
|
+
inLiteral = this.doLiteral;
|
726
|
+
literalPartStack = [];
|
727
|
+
literalParts = [];
|
728
|
+
lockLiteral = false;
|
729
|
+
targetStack = [];
|
730
|
+
}
|
731
|
+
skipBlock() {
|
732
|
+
if (index > start) {
|
733
|
+
this.sourceToTarget();
|
734
|
+
}
|
735
|
+
let depth = 1;
|
736
|
+
while (depth) {
|
737
|
+
index = source.indexOf('<!--', index);
|
738
|
+
if (index < 0) {
|
739
|
+
break;
|
740
|
+
}
|
741
|
+
index += 4;
|
742
|
+
const char = source[index];
|
743
|
+
if (!this.startsExpression(char)) {
|
744
|
+
continue;
|
745
|
+
}
|
746
|
+
if ((char === 'e') && (source.substring(index, index + 6) === 'end-->')) {
|
747
|
+
depth--;
|
748
|
+
continue;
|
749
|
+
}
|
750
|
+
depth++;
|
751
|
+
}
|
752
|
+
index -= 4;
|
753
|
+
if (index < 0) {
|
754
|
+
index = length;
|
755
|
+
}
|
756
|
+
start = index;
|
757
|
+
}
|
758
|
+
sourceToTarget() {
|
759
|
+
target += source.substring(start, index);
|
760
|
+
start = index;
|
761
|
+
}
|
762
|
+
startsExpression(char, open = '{', close = '}') {
|
763
|
+
return RegExp('[a-z0-9"%*.?@\'' + open + close + '-]', 'i').test(char);
|
764
|
+
}
|
765
|
+
trimEndLine(string) {
|
766
|
+
let index = string.length;
|
767
|
+
while ((index > 0) && ' \n\r\t\f'.includes(string[index - 1])) {
|
768
|
+
index--;
|
769
|
+
if (string[index] === '\n') {
|
770
|
+
break;
|
771
|
+
}
|
772
|
+
}
|
773
|
+
return string.substring(0, index);
|
774
|
+
}
|
775
|
+
}
|
776
|
+
exports.default = Template;
|
777
|
+
exports.Template = Template;
|
package/package.json
CHANGED
@@ -16,9 +16,17 @@
|
|
16
16
|
"ts-jest": "^29.2",
|
17
17
|
"typescript": "~5.6"
|
18
18
|
},
|
19
|
+
"engines": {
|
20
|
+
"node": ">=18"
|
21
|
+
},
|
22
|
+
"exports": {
|
23
|
+
"import": "./template.js",
|
24
|
+
"require": "./cjs/template.js"
|
25
|
+
},
|
19
26
|
"files": [
|
20
27
|
"LICENSE",
|
21
28
|
"README.md",
|
29
|
+
"cjs",
|
22
30
|
"template.d.ts",
|
23
31
|
"template.js"
|
24
32
|
],
|
@@ -41,12 +49,14 @@
|
|
41
49
|
"W3C"
|
42
50
|
],
|
43
51
|
"license": "LGPL-3.0-or-later",
|
44
|
-
"main": "./template.js",
|
45
52
|
"name": "@itrocks/template",
|
46
53
|
"repository": "https://github.com/itrocks-ts/template",
|
47
54
|
"scripts": {
|
48
|
-
"build": "
|
55
|
+
"build": "npm run build:cjs && npm run build:esm",
|
56
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
57
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
49
58
|
"test": "jest"
|
50
59
|
},
|
51
|
-
"
|
60
|
+
"types": "./template.d.ts",
|
61
|
+
"version": "0.0.6"
|
52
62
|
}
|
package/template.d.ts
CHANGED
@@ -18,6 +18,7 @@ export default class Template {
|
|
18
18
|
onTagOpened?: ((name: string) => void);
|
19
19
|
onTagClose?: ((name: string) => void);
|
20
20
|
parsers: VariableParser[];
|
21
|
+
prefixes: string;
|
21
22
|
unclosingTags: SortedArray<string>;
|
22
23
|
constructor(data?: any, containerData?: any);
|
23
24
|
applyLiterals(text: string, parts?: string[]): string;
|
package/template.js
CHANGED
@@ -1,11 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
const sorted_array_1 = require("@itrocks/sorted-array");
|
7
|
-
const promises_1 = require("node:fs/promises");
|
8
|
-
const node_path_1 = require("node:path");
|
1
|
+
import Str from '@itrocks/rename';
|
2
|
+
import appDir from '@itrocks/app-dir';
|
3
|
+
import { SortedArray } from '@itrocks/sorted-array';
|
4
|
+
import { readFile } from 'node:fs/promises';
|
5
|
+
import { normalize, sep } from 'node:path';
|
9
6
|
let blockBack;
|
10
7
|
let blockStack;
|
11
8
|
let doHeadLinks = false;
|
@@ -21,14 +18,15 @@ let lockLiteral;
|
|
21
18
|
let literalPartStack;
|
22
19
|
let literalParts;
|
23
20
|
let inLiteral;
|
24
|
-
|
25
|
-
|
26
|
-
let doneLinks = new
|
27
|
-
let headLinks = new
|
21
|
+
export const frontScripts = new SortedArray();
|
22
|
+
frontScripts.distinct = true;
|
23
|
+
let doneLinks = new SortedArray();
|
24
|
+
let headLinks = new SortedArray();
|
28
25
|
let headTitle = undefined;
|
29
26
|
doneLinks.distinct = true;
|
30
27
|
headLinks.distinct = true;
|
31
|
-
|
28
|
+
export { Template };
|
29
|
+
export default class Template {
|
32
30
|
data;
|
33
31
|
containerData;
|
34
32
|
doExpression = true;
|
@@ -37,19 +35,19 @@ class Template {
|
|
37
35
|
filePath;
|
38
36
|
included = false;
|
39
37
|
// Inline elements are replaced by $1 when in literal.
|
40
|
-
|
41
|
-
inlineElements = new sorted_array_1.SortedArray('a', 'b', 'big', 'button', 'cite', 'code', 'data', 'del', 'em', 'font', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'mark', 'meter', 'optgroup', 'option', 'output', 'picture', 'q', 'rt', 'samp', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'svg', 'time', 'tspan', 'u', 'var', 'wbr');
|
38
|
+
inlineElements = new SortedArray('a', 'abbr', 'acronym', 'b', 'bdo', 'big', 'button', 'cite', 'code', 'data', 'del', 'dfn', 'em', 'font', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'mark', 'meter', 'object', 'optgroup', 'option', 'output', 'picture', 'q', 'rt', 'samp', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'svg', 'time', 'tspan', 'tt', 'u', 'var', 'wbr');
|
42
39
|
// These attribute values are literals.
|
43
|
-
literalAttributes = new
|
40
|
+
literalAttributes = new SortedArray('alt', 'enterkeyhint', 'label', 'lang', 'placeholder', 'srcdoc', 'title');
|
44
41
|
// These element contents are literals.
|
45
|
-
literalElements = new
|
42
|
+
literalElements = new SortedArray('a', 'abbr', 'acronym', 'article', 'aside', 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'caption', 'center', 'cite', 'data', 'datalist', 'dd', 'del', 'desc', 'details', 'dfn', 'dialog', 'div', 'dt', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hr', 'i', 'iframe', 'ins', 'keygen', 'label', 'legend', 'li', 'main', 'mark', 'menuitem', 'meter', 'nav', 'noframes', 'noscript', 'optgroup', 'option', 'p', 'pre', 'q', 'rb', 's', 'section', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'summary', 'sup', 'td', 'template', 'text', 'textarea', 'textpath', 'th', 'time', 'title', 'tspan', 'u', 'wbr');
|
46
43
|
onAttribute;
|
47
44
|
onTagOpen;
|
48
45
|
onTagOpened;
|
49
46
|
onTagClose;
|
50
47
|
parsers = [];
|
48
|
+
prefixes;
|
51
49
|
// These elements have no closing tag.
|
52
|
-
unclosingTags = new
|
50
|
+
unclosingTags = new SortedArray('area', 'base', 'basefont', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track');
|
53
51
|
constructor(data, containerData) {
|
54
52
|
this.data = data;
|
55
53
|
this.containerData = containerData;
|
@@ -57,6 +55,7 @@ class Template {
|
|
57
55
|
if (containerData) {
|
58
56
|
blockStack.push({ blockStart: 0, collection: [], data: containerData, iteration: 0, iterations: 1 });
|
59
57
|
}
|
58
|
+
this.prefixes = this.parsers.map(([prefix]) => prefix).join('');
|
60
59
|
}
|
61
60
|
applyLiterals(text, parts = []) {
|
62
61
|
return text.replace(/\$([0-9]+)/g, (_, index) => parts[+index]);
|
@@ -102,8 +101,8 @@ class Template {
|
|
102
101
|
this.onTagClose = (name) => console.log('tag.closed =', name);
|
103
102
|
}
|
104
103
|
getCleanContext() {
|
105
|
-
const doneLinks = new
|
106
|
-
const headLinks = new
|
104
|
+
const doneLinks = new SortedArray;
|
105
|
+
const headLinks = new SortedArray;
|
107
106
|
doneLinks.distinct = true;
|
108
107
|
headLinks.distinct = true;
|
109
108
|
return {
|
@@ -145,7 +144,7 @@ class Template {
|
|
145
144
|
template.onTagOpen = this.onTagOpen;
|
146
145
|
template.onTagOpened = this.onTagOpened;
|
147
146
|
template.parsers = this.parsers;
|
148
|
-
const parsed = await template.parseFile(((path[0] ===
|
147
|
+
const parsed = await template.parseFile(((path[0] === sep) || (path[1] === ':')) ? path : (this.filePath + sep + path));
|
149
148
|
({
|
150
149
|
doHeadLinks, index, length, source, start, tagName, tagStack, target, targetStack,
|
151
150
|
literalParts, literalPartStack, inLiteral, lockLiteral
|
@@ -194,9 +193,9 @@ class Template {
|
|
194
193
|
if (headLinks.length) {
|
195
194
|
const position = target.lastIndexOf('>', target.indexOf('</head>')) + 1;
|
196
195
|
target = target.slice(0, position) + '\n\t' + headLinks.join('\n\t') + target.slice(position);
|
197
|
-
doneLinks = new
|
196
|
+
doneLinks = new SortedArray;
|
198
197
|
doneLinks.distinct = true;
|
199
|
-
headLinks = new
|
198
|
+
headLinks = new SortedArray;
|
200
199
|
headLinks.distinct = true;
|
201
200
|
}
|
202
201
|
if (headTitle && !this.included) {
|
@@ -306,11 +305,11 @@ class Template {
|
|
306
305
|
if (containerFileName && !this.included) {
|
307
306
|
const data = this.data;
|
308
307
|
this.data = Object.assign({ content: () => this.include(fileName, data) }, blockStack[0]?.data);
|
309
|
-
return this.parseFile(
|
308
|
+
return this.parseFile(normalize(containerFileName));
|
310
309
|
}
|
311
|
-
this.fileName = fileName.substring(fileName.lastIndexOf(
|
312
|
-
this.filePath = fileName.substring(0, fileName.lastIndexOf(
|
313
|
-
return this.parseBuffer(await
|
310
|
+
this.fileName = fileName.substring(fileName.lastIndexOf(sep) + 1);
|
311
|
+
this.filePath = fileName.substring(0, fileName.lastIndexOf(sep));
|
312
|
+
return this.parseBuffer(await readFile(fileName, 'utf-8'));
|
314
313
|
}
|
315
314
|
async parsePath(expression, data) {
|
316
315
|
if (expression === '') {
|
@@ -352,7 +351,7 @@ class Template {
|
|
352
351
|
}
|
353
352
|
}
|
354
353
|
if (data[variable] === undefined) {
|
355
|
-
data = new
|
354
|
+
data = new Str(data);
|
356
355
|
}
|
357
356
|
let value = data[variable];
|
358
357
|
return ((typeof value === 'function') && !value.prototype)
|
@@ -482,7 +481,7 @@ class Template {
|
|
482
481
|
inHead = false;
|
483
482
|
if (!doHeadLinks) {
|
484
483
|
doneLinks = headLinks;
|
485
|
-
headLinks = new
|
484
|
+
headLinks = new SortedArray;
|
486
485
|
headLinks.distinct = true;
|
487
486
|
}
|
488
487
|
}
|
@@ -607,21 +606,21 @@ class Template {
|
|
607
606
|
this.literalTarget(index);
|
608
607
|
}
|
609
608
|
if (inLinkHRef && attributeValue.endsWith('.css')) {
|
610
|
-
let frontStyle =
|
611
|
-
.substring(
|
612
|
-
if (
|
613
|
-
frontStyle = frontStyle.replaceAll(
|
609
|
+
let frontStyle = normalize(this.filePath + sep + source.substring(start, index))
|
610
|
+
.substring(appDir.length);
|
611
|
+
if (sep !== '/') {
|
612
|
+
frontStyle = frontStyle.replaceAll(sep, '/');
|
614
613
|
}
|
615
614
|
target += frontStyle;
|
616
615
|
start = index;
|
617
616
|
}
|
618
617
|
if (inScriptSrc && attributeValue.endsWith('.js')) {
|
619
|
-
let frontScript =
|
620
|
-
.substring(
|
621
|
-
if (
|
622
|
-
frontScript = frontScript.replaceAll(
|
618
|
+
let frontScript = normalize(this.filePath + sep + source.substring(start, index))
|
619
|
+
.substring(appDir.length);
|
620
|
+
if (sep !== '/') {
|
621
|
+
frontScript = frontScript.replaceAll(sep, '/');
|
623
622
|
}
|
624
|
-
|
623
|
+
frontScripts.insert(frontScript);
|
625
624
|
target += frontScript;
|
626
625
|
start = index;
|
627
626
|
}
|
@@ -772,5 +771,3 @@ class Template {
|
|
772
771
|
return string.substring(0, index);
|
773
772
|
}
|
774
773
|
}
|
775
|
-
exports.default = Template;
|
776
|
-
exports.Template = Template;
|