@itrocks/template 0.2.0 → 0.2.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/package.json CHANGED
@@ -69,5 +69,5 @@
69
69
  "test": "test/test"
70
70
  },
71
71
  "types": "./esm/template.d.ts",
72
- "version": "0.2.0"
72
+ "version": "0.2.1"
73
73
  }
@@ -0,0 +1,123 @@
1
+ import { SortedArray } from '@itrocks/sorted-array';
2
+ export type Dependencies = {
3
+ toString: (value: any) => Promise<string>;
4
+ };
5
+ export declare const depends: Dependencies;
6
+ type BlockStackEntry = {
7
+ blockStart: number;
8
+ condition?: boolean;
9
+ data: any;
10
+ iteration: IteratorResult<any> | {
11
+ done: boolean;
12
+ value?: any;
13
+ };
14
+ iterator?: Iterator<any>;
15
+ };
16
+ type Close = ')' | '}';
17
+ type Final = '' | '-->';
18
+ type Open = '(' | '{';
19
+ export type VariableParser = [parser: string, (variable: string, data: any) => any];
20
+ export declare const frontScripts: SortedArray<string>;
21
+ export declare function templateDependsOn(dependencies: Partial<Dependencies>): void;
22
+ export declare class HtmlResponse {
23
+ html: string;
24
+ dependencies: string[];
25
+ constructor(html: string, ...dependencies: string[]);
26
+ toString(): string;
27
+ }
28
+ export declare class Template {
29
+ data?: any | undefined;
30
+ containerData?: any | undefined;
31
+ blockBack: number;
32
+ blockStack: BlockStackEntry[];
33
+ doExpression: boolean;
34
+ index: number;
35
+ length: number;
36
+ source: string;
37
+ start: number;
38
+ tagName: string;
39
+ tagStack: {
40
+ tagName: string;
41
+ inLiteral: boolean;
42
+ }[];
43
+ target: string;
44
+ targetReplace: string;
45
+ targetStack: string[];
46
+ doLiteral: boolean;
47
+ inLiteral: boolean;
48
+ literalParts: string[];
49
+ literalPartStack: string[][];
50
+ lockLiteral: boolean;
51
+ addLinks: SortedArray<string>;
52
+ doneLinks: SortedArray<string>;
53
+ headTitle?: string;
54
+ fileName?: string;
55
+ filePath?: string;
56
+ included: boolean;
57
+ inlineElements: SortedArray<string>;
58
+ literalAttributes: SortedArray<string>;
59
+ literalElements: SortedArray<string>;
60
+ unclosingTags: SortedArray<string>;
61
+ onAttribute?: (name: string, value: string) => void;
62
+ onTagOpen?: (name: string) => void;
63
+ onTagOpened?: (name: string) => void;
64
+ onTagClose?: (name: string) => void;
65
+ parsers: VariableParser[];
66
+ prefixes: string;
67
+ constructor(data?: any | undefined, containerData?: any | undefined);
68
+ applyLiterals(text: string, parts?: string[]): string;
69
+ closeTag(shouldInLiteral: boolean, targetIndex: number): boolean;
70
+ combineLiterals(text: string, parts?: string[]): string;
71
+ debugEvents(): void;
72
+ embedHtmlResponse(htmlResponse: HtmlResponse): void;
73
+ getCleanContext(): {
74
+ addLinks: SortedArray<string>;
75
+ doneLinks: SortedArray<string>;
76
+ included: boolean;
77
+ index: number;
78
+ inLiteral: boolean;
79
+ length: number;
80
+ literalPartStack: never[];
81
+ literalParts: never[];
82
+ source: string;
83
+ start: number;
84
+ target: string;
85
+ targetStack: never[];
86
+ };
87
+ getPosition(): {
88
+ index: number;
89
+ start: number;
90
+ target: string;
91
+ };
92
+ getContext(): {
93
+ addLinks: SortedArray<string>;
94
+ doneLinks: SortedArray<string>;
95
+ included: boolean;
96
+ index: number;
97
+ inLiteral: boolean;
98
+ length: number;
99
+ literalParts: string[];
100
+ literalPartStack: string[][];
101
+ source: string;
102
+ start: number;
103
+ target: string;
104
+ targetStack: string[];
105
+ };
106
+ include(path: string, data: any): Promise<string>;
107
+ includePath(filePath: string): string;
108
+ isContextClean(): boolean;
109
+ literalTarget(index: number, isTitle?: boolean): void;
110
+ normalizeLink(link: string): string;
111
+ parseBuffer(buffer: string): Promise<string>;
112
+ parseExpression(data: any, open: Open | '<', close: Close, finalClose?: Final): Promise<boolean | undefined>;
113
+ parseFile(fileName: string, containerFileName?: string | false): Promise<string>;
114
+ parsePath(expression: string, data: any): Promise<any>;
115
+ parseVariable(variable: string, data: any): Promise<any>;
116
+ parseVars(): Promise<string>;
117
+ setSource(source: string, index?: number, start?: number, target?: string): void;
118
+ skipBlock(): void;
119
+ sourceToTarget(): void;
120
+ startsExpression(char: string, open?: Open, close?: Close): boolean;
121
+ trimEndLine(string: string): string;
122
+ }
123
+ export {};
@@ -0,0 +1,952 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Template = exports.HtmlResponse = exports.frontScripts = exports.depends = void 0;
4
+ exports.templateDependsOn = templateDependsOn;
5
+ const app_dir_1 = require("@itrocks/app-dir");
6
+ const rename_1 = require("@itrocks/rename");
7
+ const sorted_array_1 = require("@itrocks/sorted-array");
8
+ const promises_1 = require("node:fs/promises");
9
+ const node_path_1 = require("node:path");
10
+ const node_path_2 = require("node:path");
11
+ exports.depends = {
12
+ toString: async (value) => '' + value
13
+ };
14
+ const DEBUG = false;
15
+ const done = { done: true };
16
+ exports.frontScripts = new sorted_array_1.SortedArray();
17
+ exports.frontScripts.distinct = true;
18
+ function templateDependsOn(dependencies) {
19
+ Object.assign(exports.depends, dependencies);
20
+ }
21
+ class HtmlResponse {
22
+ html;
23
+ dependencies;
24
+ constructor(html, ...dependencies) {
25
+ this.html = html;
26
+ this.dependencies = dependencies;
27
+ }
28
+ toString() { return this.html; }
29
+ }
30
+ exports.HtmlResponse = HtmlResponse;
31
+ class Template {
32
+ data;
33
+ containerData;
34
+ // block stack
35
+ blockBack = 0;
36
+ blockStack = [];
37
+ // parser
38
+ doExpression = true;
39
+ index = 0;
40
+ length = 0;
41
+ source = '';
42
+ start = 0;
43
+ tagName = '';
44
+ tagStack = [];
45
+ target = '';
46
+ targetReplace = '';
47
+ targetStack = [];
48
+ // literal
49
+ doLiteral = false;
50
+ inLiteral = false;
51
+ literalParts = [];
52
+ literalPartStack = [];
53
+ lockLiteral = false;
54
+ // html head
55
+ addLinks = new sorted_array_1.SortedArray();
56
+ doneLinks = new sorted_array_1.SortedArray();
57
+ headTitle;
58
+ // file
59
+ fileName;
60
+ filePath;
61
+ included = false;
62
+ // Inline elements are replaced by $1 when in literal.
63
+ 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');
64
+ // These attribute values are literals.
65
+ literalAttributes = new sorted_array_1.SortedArray('alt', 'enterkeyhint', 'label', 'lang', 'placeholder', 'srcdoc', 'title');
66
+ // These element contents are literals.
67
+ 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');
68
+ // These elements have no closing tag.
69
+ unclosingTags = new sorted_array_1.SortedArray('area', 'base', 'basefont', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track');
70
+ // Event hooks
71
+ onAttribute;
72
+ onTagOpen;
73
+ onTagOpened;
74
+ onTagClose;
75
+ // Additional parsers
76
+ parsers = [];
77
+ prefixes = '';
78
+ constructor(data, containerData) {
79
+ this.data = data;
80
+ this.containerData = containerData;
81
+ this.addLinks.distinct = true;
82
+ this.doneLinks.distinct = true;
83
+ if (containerData) {
84
+ this.blockStack.push({ blockStart: 0, data: containerData, iteration: done });
85
+ }
86
+ }
87
+ applyLiterals(text, parts = []) {
88
+ return text.replace(/\$([0-9]+)/g, (_, index) => parts[+index]);
89
+ }
90
+ closeTag(shouldInLiteral, targetIndex) {
91
+ shouldInLiteral ||= this.inLiteral;
92
+ Object.assign(this, this.tagStack.pop() ?? { tagName: '', inLiteral: false });
93
+ if (this.onTagClose)
94
+ this.onTagClose.call(this, this.tagName);
95
+ if ((this.tagName[0] === 'a') && (this.tagName === 'address')) {
96
+ this.lockLiteral = false;
97
+ }
98
+ if (this.inLiteral && this.inlineElements.includes(this.tagName)) {
99
+ if (this.literalElements.includes(this.tagName)) {
100
+ this.literalTarget(targetIndex);
101
+ }
102
+ this.literalParts = this.literalPartStack.pop();
103
+ this.literalParts.push(this.target + this.source.substring(this.start, this.index));
104
+ this.start = this.index;
105
+ this.target = this.targetStack.pop() + '$' + this.literalParts.length;
106
+ shouldInLiteral = false;
107
+ }
108
+ return shouldInLiteral;
109
+ }
110
+ combineLiterals(text, parts) {
111
+ const original = text;
112
+ text = text.trimEnd();
113
+ const right = text.length;
114
+ let left = text.length;
115
+ text = text.trimStart();
116
+ left -= text.length;
117
+ if (text !== '') {
118
+ text = (parts && /^(\$[1-9][0-9]*)+$/.test(text))
119
+ ? parts.join('')
120
+ : this.applyLiterals(text, parts?.map(part => (((typeof part)[0] === 's') && (part[0] !== '<')) ? this.applyLiterals(part) : part));
121
+ }
122
+ return original.substring(0, left) + text + original.substring(right);
123
+ }
124
+ debugEvents() {
125
+ this.onAttribute = (name, value) => console.log('attribute', name, '=', value);
126
+ this.onTagOpen = (name) => console.log('tag.open =', name);
127
+ this.onTagOpened = (name) => console.log('tag.opened =', name);
128
+ this.onTagClose = (name) => console.log('tag.closed =', name);
129
+ }
130
+ embedHtmlResponse(htmlResponse) {
131
+ for (let dependency of htmlResponse.dependencies) {
132
+ if (dependency[0] === '<') {
133
+ const script = dependency.match(/<script[^>]*\bsrc=["']([^"']+)["']/i)?.[1];
134
+ if (script) {
135
+ exports.frontScripts.push(script);
136
+ }
137
+ if (DEBUG)
138
+ console.log('addLink(', dependency, ')');
139
+ this.addLinks.push(dependency);
140
+ continue;
141
+ }
142
+ dependency = (0, node_path_1.normalize)(dependency).slice(app_dir_1.appDir.length);
143
+ switch (dependency.slice(dependency.lastIndexOf('.') + 1)) {
144
+ case 'css':
145
+ if (DEBUG)
146
+ console.log('addLink(', '<link href="' + dependency + '" rel="stylesheet">', ')');
147
+ this.addLinks.push('<link href="' + dependency + '" rel="stylesheet">');
148
+ continue;
149
+ case 'js':
150
+ exports.frontScripts.push(dependency);
151
+ if (DEBUG)
152
+ console.log('addLink(', '<script src="' + dependency + '" type="module"></script>', ')');
153
+ this.addLinks.push('<script src="' + dependency + '" type="module"></script>');
154
+ continue;
155
+ }
156
+ }
157
+ }
158
+ getCleanContext() {
159
+ const addLinks = new sorted_array_1.SortedArray;
160
+ const doneLinks = new sorted_array_1.SortedArray;
161
+ addLinks.distinct = true;
162
+ doneLinks.distinct = true;
163
+ return {
164
+ addLinks,
165
+ doneLinks,
166
+ included: false,
167
+ index: this.length,
168
+ inLiteral: this.doLiteral,
169
+ length: this.source.length,
170
+ literalPartStack: [],
171
+ literalParts: [],
172
+ source: this.source,
173
+ start: this.length,
174
+ target: this.target,
175
+ targetStack: []
176
+ };
177
+ }
178
+ getPosition() {
179
+ return { index: this.index, start: this.start, target: this.target };
180
+ }
181
+ getContext() {
182
+ return {
183
+ addLinks: this.addLinks,
184
+ doneLinks: this.doneLinks,
185
+ included: this.included,
186
+ index: this.index,
187
+ inLiteral: this.inLiteral,
188
+ length: this.length,
189
+ literalParts: this.literalParts,
190
+ literalPartStack: this.literalPartStack,
191
+ source: this.source,
192
+ start: this.start,
193
+ target: this.target,
194
+ targetStack: this.targetStack,
195
+ };
196
+ }
197
+ async include(path, data) {
198
+ const template = new (Object.getPrototypeOf(this).constructor)(data, this.blockStack[0]?.data);
199
+ template.addLinks = this.addLinks;
200
+ template.doExpression = this.doExpression;
201
+ template.doLiteral = this.doLiteral;
202
+ template.doneLinks = this.doneLinks;
203
+ template.included = true;
204
+ template.onAttribute = this.onAttribute;
205
+ template.onTagClose = this.onTagClose;
206
+ template.onTagOpen = this.onTagOpen;
207
+ template.onTagOpened = this.onTagOpened;
208
+ template.parsers = this.parsers;
209
+ const parsed = await template.parseFile(((path[0] === node_path_2.sep) || (path[1] === ':'))
210
+ ? path
211
+ : (this.filePath + node_path_2.sep + path));
212
+ this.headTitle = template.headTitle;
213
+ const beginPosition = parsed.indexOf('<!--BEGIN-->');
214
+ const endPosition = parsed.lastIndexOf('<!--END-->');
215
+ if ((beginPosition < 0) && (parsed[1] === '!') && parsed.startsWith('<!DOCTYPE html>')) {
216
+ if (this.targetReplace === '') {
217
+ if (DEBUG)
218
+ console.log('! targetReplace', path);
219
+ this.targetReplace = parsed;
220
+ }
221
+ return '';
222
+ }
223
+ return (beginPosition > -1)
224
+ ? parsed.slice(beginPosition + 12, (endPosition > -1) ? endPosition : parsed.length)
225
+ : parsed;
226
+ }
227
+ includePath(filePath) {
228
+ return (filePath[0] === '/')
229
+ ? (app_dir_1.appDir + ((filePath[1] === '@') ? '/node_modules' : '') + filePath)
230
+ : filePath;
231
+ }
232
+ isContextClean() {
233
+ const clean = this.getCleanContext();
234
+ const context = this.getContext();
235
+ return context.addLinks.distinct === clean.addLinks.distinct
236
+ && context.addLinks.length === clean.addLinks.length
237
+ && context.doneLinks.distinct === clean.doneLinks.distinct
238
+ && context.included === clean.included
239
+ && context.index === clean.index
240
+ && context.inLiteral === clean.inLiteral
241
+ && context.literalPartStack.length === clean.literalPartStack.length
242
+ && context.literalParts.length === clean.literalParts.length
243
+ && context.length === clean.length
244
+ && context.start === clean.start
245
+ && context.targetStack.length === clean.targetStack.length;
246
+ }
247
+ literalTarget(index, isTitle = false) {
248
+ let combined;
249
+ if (this.literalParts.length) {
250
+ this.target += this.source.substring(this.start, index);
251
+ combined = this.combineLiterals(this.target, this.literalParts);
252
+ this.target = (this.targetStack.pop() ?? '') + combined;
253
+ this.literalParts = [];
254
+ }
255
+ else {
256
+ combined = this.combineLiterals(this.source.substring(this.start, index));
257
+ this.target += combined;
258
+ }
259
+ if (isTitle && this.included) {
260
+ this.headTitle = combined;
261
+ }
262
+ this.start = index;
263
+ }
264
+ normalizeLink(link) {
265
+ if (link[0] === '/')
266
+ return link;
267
+ const result = (0, node_path_1.normalize)(this.filePath + node_path_2.sep + link).substring(app_dir_1.appDir.length);
268
+ return (((node_path_2.sep === '/') ? result : result.replaceAll(node_path_2.sep, '/')))
269
+ .replace(/^\/node_modules\/@itrocks\//, '/@itrocks/')
270
+ .replace(/^\/node_modules\//, '/lib/');
271
+ }
272
+ async parseBuffer(buffer) {
273
+ this.prefixes = this.parsers.map(([prefix]) => prefix).join('');
274
+ this.setSource(buffer);
275
+ await this.parseVars();
276
+ if (this.included) {
277
+ return this.target;
278
+ }
279
+ if (this.addLinks.length) {
280
+ let addLink;
281
+ const addLinks = new Array;
282
+ while (addLink = this.addLinks.shift()) {
283
+ for (const attribute of ['href', 'src']) {
284
+ let start = addLink.indexOf(attribute + '=');
285
+ if (start < 0)
286
+ continue;
287
+ start += attribute.length;
288
+ while ((addLink[start] !== '"') && (addLink[start] !== "'")) {
289
+ start++;
290
+ }
291
+ const quote = addLink[start++];
292
+ const stop = addLink.indexOf(quote, start);
293
+ const link = addLink.substring(start, stop);
294
+ if (DEBUG)
295
+ console.log('check(', link, ')');
296
+ if (!this.doneLinks.includes(link)) {
297
+ if (DEBUG)
298
+ console.log('+ addLink(', addLink, ')');
299
+ if (DEBUG)
300
+ console.log('+ doneLink(', link, ')');
301
+ addLinks.push(addLink);
302
+ this.doneLinks.push(link);
303
+ }
304
+ }
305
+ }
306
+ if (addLinks.length) {
307
+ const position = this.target.lastIndexOf('>', this.target.indexOf('</head>')) + 1;
308
+ this.target = this.target.slice(0, position) + '\n\t' + addLinks.join('\n\t') + this.target.slice(position);
309
+ }
310
+ }
311
+ return (this.targetReplace !== '') ? this.targetReplace : this.target;
312
+ }
313
+ async parseExpression(data, open, close, finalClose = '') {
314
+ const indexOut = this.index;
315
+ if (this.inLiteral && !this.literalParts.length) {
316
+ this.targetStack.push(this.target);
317
+ this.target = '';
318
+ }
319
+ if (open === '<') {
320
+ this.index += 3;
321
+ open = '{';
322
+ }
323
+ this.index++;
324
+ const firstChar = this.source[this.index];
325
+ if ((this.index >= this.length) || !this.startsExpression(firstChar, open, close)) {
326
+ return;
327
+ }
328
+ let conditional = (firstChar === '?');
329
+ const finalChar = finalClose.length ? finalClose[0] : '';
330
+ let stackPos = this.targetStack.length;
331
+ if (conditional) {
332
+ this.index++;
333
+ }
334
+ this.targetStack.push(this.target + this.source.substring(this.start, indexOut));
335
+ this.start = this.index;
336
+ this.target = '';
337
+ while (this.index < this.length) {
338
+ const char = this.source[this.index];
339
+ if (char === open) {
340
+ this.targetStack.push(this.target + this.source.substring(this.start, this.index));
341
+ this.index++;
342
+ this.start = this.index;
343
+ this.target = '';
344
+ continue;
345
+ }
346
+ if ((char === close)
347
+ || ((char === finalChar) && (this.source.substring(this.index, this.index + finalClose.length) === finalClose))) {
348
+ let minus = 0;
349
+ if (this.source[this.index - 1] === '?') {
350
+ conditional = true;
351
+ minus = 1;
352
+ }
353
+ const expression = this.target + this.source.substring(this.start, this.index - minus);
354
+ const lastTarget = this.targetStack.pop();
355
+ const parsed = await this.parsePath(expression, data);
356
+ this.index += (char === close) ? 1 : finalClose.length;
357
+ this.start = this.index;
358
+ this.target = '';
359
+ if (char === finalChar)
360
+ while (this.targetStack.length > stackPos) {
361
+ this.target += this.targetStack.shift();
362
+ }
363
+ if (this.inLiteral && (this.targetStack.length === stackPos)) {
364
+ this.literalParts.push(parsed);
365
+ this.target += lastTarget + '$' + this.literalParts.length;
366
+ return conditional;
367
+ }
368
+ if (lastTarget.length || this.target.length) {
369
+ this.target += lastTarget + parsed;
370
+ }
371
+ else {
372
+ this.target = parsed;
373
+ }
374
+ if (this.targetStack.length !== stackPos) {
375
+ continue;
376
+ }
377
+ if (conditional && !parsed) {
378
+ if ((typeof this.target)[0] === 's') {
379
+ this.target = this.target.substring(0, this.target.lastIndexOf(' '));
380
+ while ((this.index < this.length) && !' >\n\r\t\f'.includes(this.source[this.index])) {
381
+ this.index++;
382
+ this.start++;
383
+ }
384
+ this.index--;
385
+ }
386
+ return conditional;
387
+ }
388
+ return conditional;
389
+ }
390
+ if ((char === '"') || (char === "'")) {
391
+ this.index++;
392
+ let c;
393
+ while ((this.index < this.length) && ((c = this.source[this.index]) !== char)) {
394
+ if (c === '\\')
395
+ this.index++;
396
+ this.index++;
397
+ }
398
+ }
399
+ this.index++;
400
+ }
401
+ // bad close
402
+ stackPos++;
403
+ while (this.targetStack.length > stackPos) {
404
+ this.target = this.targetStack.pop() + open + this.target;
405
+ }
406
+ this.target = this.targetStack.pop() + (finalClose.length ? '<!--' : open) + this.target;
407
+ return conditional;
408
+ }
409
+ async parseFile(fileName, containerFileName) {
410
+ if (DEBUG)
411
+ console.log('----- parseFile', containerFileName ? 'contained' : 'fetched', this.included ? 'included' : 'final', fileName);
412
+ if (containerFileName) {
413
+ const data = this.data;
414
+ this.data = Object.assign({ content: () => this.include(fileName, data) }, this.blockStack[0]?.data);
415
+ return this.parseFile((0, node_path_1.normalize)(containerFileName));
416
+ }
417
+ this.fileName = fileName.substring(fileName.lastIndexOf(node_path_2.sep) + 1);
418
+ this.filePath = fileName.substring(0, fileName.lastIndexOf(node_path_2.sep));
419
+ let target = await this.parseBuffer(await (0, promises_1.readFile)(fileName, 'utf-8'));
420
+ if (containerFileName && this.headTitle) {
421
+ const position = target.indexOf('>', target.indexOf('<title') + 6) + 1;
422
+ target = target.slice(0, position)
423
+ + this.headTitle
424
+ + target.slice(target.indexOf('</title>', position));
425
+ }
426
+ return target;
427
+ }
428
+ async parsePath(expression, data) {
429
+ if (DEBUG)
430
+ console.log('parsePath', expression);
431
+ if (expression === '') {
432
+ return undefined;
433
+ }
434
+ if (((expression[0] === '.') && ((expression[1] === '/') || ((expression[1] === '.') && (expression[2] === '/'))))
435
+ || (expression[0] === '/')) {
436
+ let expressionEnd = expression.length - 1;
437
+ if (expression[expressionEnd] === '-') {
438
+ let blockBack = 1;
439
+ expressionEnd--;
440
+ while (expression[expressionEnd] === '-') {
441
+ blockBack++;
442
+ expressionEnd--;
443
+ }
444
+ const blockStack = this.blockStack;
445
+ return this.include(this.includePath(expression.slice(0, expressionEnd)), blockStack[blockStack.length - blockBack].data);
446
+ }
447
+ if (expression[expressionEnd] === ')') {
448
+ const openPosition = expression.lastIndexOf('(');
449
+ return this.include(this.includePath(expression.slice(0, openPosition)), await this.parsePath(expression.slice(openPosition + 1, expression.length - 1), data));
450
+ }
451
+ return this.include(this.includePath(expression), data);
452
+ }
453
+ let onlyDots = true;
454
+ for (const c of expression) {
455
+ if (c === '.')
456
+ continue;
457
+ onlyDots = false;
458
+ break;
459
+ }
460
+ if (onlyDots) {
461
+ if (expression.length <= 1) {
462
+ return (((typeof data)[0] === 'f') && ((data + '')[0] !== 'c'))
463
+ ? data.call()
464
+ : data;
465
+ }
466
+ expression = expression.slice(2);
467
+ }
468
+ this.blockBack = 0;
469
+ for (const variable of expression.split('.')) {
470
+ data = await this.parseVariable(variable, data);
471
+ }
472
+ if (data instanceof HtmlResponse) {
473
+ this.embedHtmlResponse(data);
474
+ }
475
+ return data;
476
+ }
477
+ async parseVariable(variable, data) {
478
+ if (DEBUG)
479
+ console.log('parseVariable', variable, 'in', data);
480
+ if (variable === '') {
481
+ let dataBack;
482
+ do {
483
+ this.blockBack++;
484
+ dataBack = this.blockStack[this.blockStack.length - this.blockBack];
485
+ } while (dataBack.condition);
486
+ return dataBack.data;
487
+ }
488
+ if (variable === '*') {
489
+ return (typeof data === 'object') ? Object.values(data) : data;
490
+ }
491
+ const firstChar = variable[0];
492
+ if ((firstChar === 'B') && (variable === 'BEGIN')) {
493
+ return data;
494
+ }
495
+ if (((firstChar === '"') && (variable[variable.length - 1] === '"'))
496
+ || ((firstChar === "'") && (variable[variable.length - 1] === "'"))) {
497
+ return variable.substring(1, variable.length - 1);
498
+ }
499
+ for (const [prefix, callback] of this.parsers) {
500
+ if (firstChar === prefix) {
501
+ return await callback(variable, data);
502
+ }
503
+ }
504
+ if (((typeof data)[0] === 'o') ? !(variable in data) : (variable[data] === undefined)) {
505
+ const asStr = new rename_1.Str(await exports.depends.toString(data));
506
+ if (DEBUG)
507
+ console.log('is', variable, 'in', asStr, '?');
508
+ if (variable in asStr) {
509
+ data = asStr;
510
+ }
511
+ }
512
+ let value = data[variable];
513
+ return (((typeof value)[0] === 'f') && ((value + '')[0] !== 'c'))
514
+ ? value.call(data)
515
+ : value;
516
+ }
517
+ async parseVars() {
518
+ let blockStart = 0;
519
+ let data = this.data;
520
+ let inHead = false;
521
+ let iteration = done;
522
+ let iterator;
523
+ while (this.index < this.length) {
524
+ let char = this.source[this.index];
525
+ // expression
526
+ if ((char === '{') && this.doExpression) {
527
+ await this.parseExpression(data, char, '}');
528
+ continue;
529
+ }
530
+ // tag ?
531
+ if (char !== '<') {
532
+ this.index++;
533
+ continue;
534
+ }
535
+ const tagIndex = this.index;
536
+ char = this.source[++this.index];
537
+ if (char === '!') {
538
+ if (this.inLiteral) {
539
+ this.literalTarget(tagIndex);
540
+ }
541
+ char = this.source[++this.index];
542
+ this.index++;
543
+ // comment tag
544
+ if ((char === '-') && (this.source[this.index] === '-')) {
545
+ this.index++;
546
+ const firstChar = this.source[this.index];
547
+ if (!this.doExpression
548
+ || !this.startsExpression(firstChar)
549
+ || ((firstChar === 'B') && (this.source.substring(this.index, this.index + 8) === 'BEGIN-->'))
550
+ || ((firstChar === 'E') && (this.source.substring(this.index, this.index + 6) === 'END-->'))) {
551
+ this.index = this.source.indexOf('-->', this.index) + 3;
552
+ if (this.index < 3)
553
+ break;
554
+ if (this.inLiteral && (this.index > this.start)) {
555
+ this.sourceToTarget();
556
+ }
557
+ continue;
558
+ }
559
+ // end condition / loop block
560
+ if ((firstChar === 'e') && (this.source.substring(this.index, this.index + 6) === 'end-->')) {
561
+ this.target += this.trimEndLine(this.source.substring(this.start, tagIndex));
562
+ iteration = iterator?.next() ?? done;
563
+ if (!iteration.done) {
564
+ data = iteration.value;
565
+ this.index = this.start = blockStart;
566
+ if (this.inLiteral && (this.index > this.start)) {
567
+ this.sourceToTarget();
568
+ }
569
+ continue;
570
+ }
571
+ ({ blockStart, data, iteration, iterator } = this.blockStack.pop()
572
+ ?? { blockStart: 0, data: undefined, iteration: done });
573
+ this.index += 6;
574
+ this.start = this.index;
575
+ if (this.inLiteral && (this.index > this.start)) {
576
+ this.sourceToTarget();
577
+ }
578
+ continue;
579
+ }
580
+ // begin condition / loop block
581
+ if (tagIndex > this.start) {
582
+ this.target += this.trimEndLine(this.source.substring(this.start, tagIndex));
583
+ this.start = tagIndex;
584
+ }
585
+ const backTarget = this.target;
586
+ const backInLiteral = this.inLiteral;
587
+ this.index = tagIndex;
588
+ this.target = '';
589
+ this.inLiteral = false;
590
+ const condition = await this.parseExpression(data, '<', '}', '-->');
591
+ this.blockStack.push({ blockStart, condition, data, iteration, iterator });
592
+ let blockData = condition ? (this.target ? data : undefined) : this.target;
593
+ blockStart = this.index;
594
+ this.target = backTarget;
595
+ this.inLiteral = backInLiteral;
596
+ if (blockData && blockData[Symbol.iterator]) {
597
+ iterator = blockData[Symbol.iterator]();
598
+ iteration = iterator?.next() ?? done;
599
+ data = iteration.value;
600
+ }
601
+ else {
602
+ data = blockData;
603
+ iteration = { done: !data, value: data };
604
+ iterator = undefined;
605
+ }
606
+ if (iteration.done) {
607
+ this.skipBlock();
608
+ continue;
609
+ }
610
+ if (this.inLiteral && (this.index > this.start)) {
611
+ this.sourceToTarget();
612
+ }
613
+ continue;
614
+ }
615
+ // cdata section
616
+ if ((char === '[') && (this.source.substring(this.index, this.index + 6) === 'CDATA[')) {
617
+ this.index = this.source.indexOf(']]>', this.index + 6) + 3;
618
+ if (this.index < 3)
619
+ break;
620
+ }
621
+ // DOCTYPE
622
+ else {
623
+ this.index = this.source.indexOf('>', this.index) + 1;
624
+ }
625
+ if (this.inLiteral) {
626
+ this.sourceToTarget();
627
+ }
628
+ continue;
629
+ }
630
+ // tag close
631
+ if (char === '/') {
632
+ this.index++;
633
+ const closeTagName = this.source.substring(this.index, this.source.indexOf('>', this.index));
634
+ this.index += closeTagName.length + 1;
635
+ if (inHead && (closeTagName[0] === 'h') && (closeTagName === 'head')) {
636
+ inHead = false;
637
+ }
638
+ let shouldInLiteral = this.inLiteral;
639
+ if (!this.unclosingTags.includes(closeTagName)) {
640
+ do {
641
+ shouldInLiteral = this.closeTag(shouldInLiteral, tagIndex);
642
+ } while ((this.tagName !== closeTagName) && this.tagName.length);
643
+ }
644
+ if (shouldInLiteral) {
645
+ this.lockLiteral = false;
646
+ this.literalTarget(tagIndex, (this.tagName[0] === 't') && (this.tagName === 'title'));
647
+ }
648
+ if (this.inLiteral && (this.index > this.start)) {
649
+ this.sourceToTarget();
650
+ }
651
+ continue;
652
+ }
653
+ // tag open
654
+ while ((this.index < this.length) && !' >\n\r\t\f'.includes(this.source[this.index]))
655
+ this.index++;
656
+ this.tagName = this.source.substring(tagIndex + 1, this.index);
657
+ if (this.onTagOpen)
658
+ this.onTagOpen.call(this, this.tagName);
659
+ while (' \n\r\t\f'.includes(this.source[this.index]))
660
+ this.index++;
661
+ char = this.tagName[0];
662
+ if ((char === 'h') && (this.tagName === 'head')) {
663
+ inHead = true;
664
+ }
665
+ const unclosingTag = this.unclosingTags.includes(this.tagName);
666
+ if (!unclosingTag) {
667
+ this.tagStack.push({ tagName: this.tagName, inLiteral: this.inLiteral });
668
+ }
669
+ let inlineElement = false;
670
+ let pushedParts = false;
671
+ if (this.inLiteral) {
672
+ inlineElement = this.inlineElements.includes(this.tagName);
673
+ if (inlineElement) {
674
+ if (this.literalParts.length) {
675
+ this.targetStack.push(this.target + this.source.substring(this.start, tagIndex));
676
+ }
677
+ else {
678
+ this.targetStack.push(this.target, this.source.substring(this.start, tagIndex));
679
+ }
680
+ this.start = tagIndex;
681
+ this.target = '';
682
+ if (!unclosingTag) {
683
+ this.literalPartStack.push(this.literalParts);
684
+ this.literalParts = [];
685
+ pushedParts = true;
686
+ }
687
+ }
688
+ else {
689
+ this.literalTarget(tagIndex);
690
+ }
691
+ }
692
+ const elementInLiteral = this.inLiteral;
693
+ // attributes
694
+ let hasTypeSubmit = false;
695
+ const inInput = (char === 'i') && (this.tagName === 'input');
696
+ const inLink = (char === 'l') && (this.tagName === 'link');
697
+ const inScript = (char === 's') && (this.tagName === 'script');
698
+ let targetTagIndex = -1;
699
+ if (inHead && (inLink || inScript)) {
700
+ this.sourceToTarget();
701
+ targetTagIndex = this.target.lastIndexOf('<');
702
+ }
703
+ while (this.source[this.index] !== '>') {
704
+ // attribute name
705
+ const attributePosition = this.index;
706
+ while ((this.index < this.length) && !' =>\n\r\t\f'.includes(this.source[this.index]))
707
+ this.index++;
708
+ const attributeName = this.source.substring(attributePosition, this.index);
709
+ while (' \n\r\t\f'.includes(this.source[this.index]))
710
+ this.index++;
711
+ let attributeBlock = (attributeName[0] === 'd') && (attributeName === 'data-if') ? '' : undefined;
712
+ // attribute value
713
+ if (this.source[this.index] === '=') {
714
+ this.index++;
715
+ while (' \n\r\t\f'.includes(this.source[this.index]))
716
+ this.index++;
717
+ const attributeChar = attributeName[0];
718
+ const [open, close] = ('afhls'.includes(attributeChar)
719
+ && ['action', 'formaction', 'href', 'location', 'src'].includes(attributeName)) ? ['(', ')']
720
+ : ['{', '}'];
721
+ let quote = this.source[this.index];
722
+ if ((quote === '"') || (quote === "'")) {
723
+ this.index++;
724
+ }
725
+ else {
726
+ quote = ' >';
727
+ }
728
+ if ((open === '(') && (this.source.substring(this.index, this.index + 6) === 'app://')) {
729
+ this.sourceToTarget();
730
+ this.index += 6;
731
+ this.start = this.index;
732
+ }
733
+ this.inLiteral = this.doLiteral && (this.literalAttributes.includes(attributeName)
734
+ || (hasTypeSubmit && (attributeChar === 'v') && (attributeName === 'value')));
735
+ if (this.inLiteral && !pushedParts && unclosingTag && this.literalParts.length) {
736
+ this.literalPartStack.push(this.literalParts);
737
+ this.literalParts = [];
738
+ pushedParts = true;
739
+ }
740
+ const inLinkHRef = inLink && (attributeChar === 'h') && (attributeName === 'href');
741
+ const inScriptSrc = inScript && (attributeChar === 's') && (attributeName === 'src');
742
+ if ((inLinkHRef || inScriptSrc || this.inLiteral) && (this.index > this.start)) {
743
+ this.sourceToTarget();
744
+ }
745
+ const valuePosition = this.index;
746
+ const shortQuote = !(quote.length - 1);
747
+ if (shortQuote && (attributeBlock !== undefined)) {
748
+ attributeBlock = this.target + this.source.substring(this.start, attributePosition);
749
+ this.start = this.index;
750
+ this.target = '';
751
+ }
752
+ while (this.index < this.length) {
753
+ const char = this.source[this.index];
754
+ // end of attribute value
755
+ if (shortQuote ? (char === quote) : quote.includes(char)) {
756
+ const attributeValue = this.source.substring(valuePosition, this.index);
757
+ if (inInput && !hasTypeSubmit) {
758
+ hasTypeSubmit = (attributeChar === 't') && (attributeValue[0] === 's')
759
+ && (attributeName === 'type') && (attributeValue === 'submit');
760
+ }
761
+ if (this.inLiteral) {
762
+ this.literalTarget(this.index);
763
+ }
764
+ if (inLinkHRef && attributeValue.endsWith('.css')) {
765
+ let frontStyle = this.normalizeLink(this.source.substring(this.start, this.index));
766
+ this.target += frontStyle;
767
+ this.start = this.index;
768
+ if (!(inHead && this.included)) {
769
+ if (DEBUG)
770
+ console.log('doneLink(', frontStyle, ')');
771
+ this.doneLinks.push(frontStyle);
772
+ }
773
+ }
774
+ if (inScriptSrc && attributeValue.endsWith('.js')) {
775
+ let frontScript = this.normalizeLink(this.source.substring(this.start, this.index));
776
+ exports.frontScripts.push(frontScript);
777
+ this.target += frontScript;
778
+ this.start = this.index;
779
+ if (!(inHead && this.included)) {
780
+ if (DEBUG)
781
+ console.log('doneLink(', frontScript, ')');
782
+ this.doneLinks.push(frontScript);
783
+ }
784
+ }
785
+ if (this.onAttribute)
786
+ this.onAttribute(attributeName, attributeValue);
787
+ if (char !== '>')
788
+ this.index++;
789
+ break;
790
+ }
791
+ // expression in attribute value
792
+ if ((char === open) && this.doExpression) {
793
+ await this.parseExpression(data, open, close);
794
+ continue;
795
+ }
796
+ this.index++;
797
+ }
798
+ }
799
+ else {
800
+ if (this.onAttribute)
801
+ this.onAttribute(attributeName, '');
802
+ if ((attributeName[0] === 'd') && (attributeName === 'data-end')) {
803
+ this.index = attributePosition;
804
+ this.sourceToTarget();
805
+ this.index += attributeName.length;
806
+ this.start = this.index;
807
+ }
808
+ }
809
+ // next attribute
810
+ while (' \n\r\t\f'.includes(this.source[this.index]))
811
+ this.index++;
812
+ if (attributeBlock !== undefined) {
813
+ if (!this.target) {
814
+ this.index = this.source.indexOf('data-end', this.index) + 8;
815
+ if (this.index < 8) {
816
+ throw 'Missing data-end matching data-if at position ' + attributePosition
817
+ + ' into template file ' + this.filePath + node_path_2.sep + this.fileName;
818
+ }
819
+ }
820
+ this.start = this.index;
821
+ this.target = attributeBlock;
822
+ }
823
+ }
824
+ this.index++;
825
+ if (this.onTagOpened)
826
+ this.onTagOpened.call(this, this.tagName);
827
+ // skip script content
828
+ if (inScript) {
829
+ if (this.onTagClose)
830
+ this.onTagClose.call(this, 'script');
831
+ this.index = this.source.indexOf('</script>', this.index) + 9;
832
+ if (this.index < 9)
833
+ break;
834
+ if (this.inLiteral && (this.index > this.start)) {
835
+ this.sourceToTarget();
836
+ }
837
+ }
838
+ if ((targetTagIndex > -1) && this.included) {
839
+ this.sourceToTarget();
840
+ const headLink = this.target.substring(targetTagIndex);
841
+ if (DEBUG)
842
+ console.log('addLink(', headLink, ')');
843
+ this.addLinks.push(headLink);
844
+ }
845
+ if (inScript) {
846
+ continue;
847
+ }
848
+ if (unclosingTag) {
849
+ if (pushedParts) {
850
+ this.literalParts = this.literalPartStack.pop();
851
+ }
852
+ this.inLiteral = elementInLiteral;
853
+ if (this.onTagClose)
854
+ this.onTagClose.call(this, this.tagName);
855
+ if (this.inLiteral) {
856
+ if (this.index > this.start) {
857
+ this.sourceToTarget();
858
+ }
859
+ if (inlineElement) {
860
+ this.literalParts.push(this.target);
861
+ this.target = this.targetStack.pop() + '$' + this.literalParts.length;
862
+ }
863
+ }
864
+ }
865
+ else {
866
+ this.lockLiteral ||= (this.tagName[0] === 'a') && (this.tagName === 'address');
867
+ this.inLiteral = this.doLiteral && !this.lockLiteral && this.literalElements.includes(this.tagName);
868
+ if (this.inLiteral && (this.index > this.start)) {
869
+ this.sourceToTarget();
870
+ }
871
+ }
872
+ }
873
+ if (this.tagStack.length) {
874
+ let shouldInLiteral = this.inLiteral;
875
+ while (this.tagStack.length) {
876
+ shouldInLiteral = this.closeTag(shouldInLiteral, this.length);
877
+ }
878
+ if (shouldInLiteral) {
879
+ this.literalTarget(this.length);
880
+ }
881
+ return this.target;
882
+ }
883
+ if (this.inLiteral) {
884
+ this.literalTarget(this.index);
885
+ }
886
+ if (this.start < this.length) {
887
+ this.target += this.source.substring(this.start);
888
+ this.start = this.length;
889
+ }
890
+ return this.target;
891
+ }
892
+ setSource(source, index = 0, start, target = '') {
893
+ this.index = index;
894
+ this.length = source.length;
895
+ this.source = source;
896
+ this.start = start ?? index;
897
+ this.tagName = '';
898
+ this.tagStack = [];
899
+ this.target = target;
900
+ this.inLiteral = this.doLiteral;
901
+ this.literalPartStack = [];
902
+ this.literalParts = [];
903
+ this.lockLiteral = false;
904
+ this.targetStack = [];
905
+ }
906
+ skipBlock() {
907
+ if (this.index > this.start) {
908
+ this.sourceToTarget();
909
+ }
910
+ let depth = 1;
911
+ while (depth) {
912
+ this.index = this.source.indexOf('<!--', this.index);
913
+ if (this.index < 0) {
914
+ break;
915
+ }
916
+ this.index += 4;
917
+ const char = this.source[this.index];
918
+ if (!this.startsExpression(char)) {
919
+ continue;
920
+ }
921
+ if ((char === 'e') && (this.source.substring(this.index, this.index + 6) === 'end-->')) {
922
+ depth--;
923
+ continue;
924
+ }
925
+ depth++;
926
+ }
927
+ this.index -= 4;
928
+ if (this.index < 0) {
929
+ this.index = this.length;
930
+ }
931
+ this.start = this.index;
932
+ }
933
+ sourceToTarget() {
934
+ this.target += this.source.substring(this.start, this.index);
935
+ this.start = this.index;
936
+ }
937
+ startsExpression(char, open = '{', close = '}') {
938
+ return RegExp(`[a-z0-9"'*./?` + open + close + this.prefixes + ']', 'i').test(char);
939
+ }
940
+ trimEndLine(string) {
941
+ let index = string.length;
942
+ while ((index > 0) && ' \n\r\t\f'.includes(string[index - 1])) {
943
+ index--;
944
+ if (string[index] === '\n') {
945
+ break;
946
+ }
947
+ }
948
+ return string.substring(0, index);
949
+ }
950
+ }
951
+ exports.Template = Template;
952
+ //# sourceMappingURL=template.js.map