@itrocks/template 0.1.7 → 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/cjs/template.js CHANGED
@@ -265,7 +265,9 @@ class Template {
265
265
  if (link[0] === '/')
266
266
  return link;
267
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, '/');
268
+ return (((node_path_2.sep === '/') ? result : result.replaceAll(node_path_2.sep, '/')))
269
+ .replace(/^\/node_modules\/@itrocks\//, '/@itrocks/')
270
+ .replace(/^\/node_modules\//, '/lib/');
269
271
  }
270
272
  async parseBuffer(buffer) {
271
273
  this.prefixes = this.parsers.map(([prefix]) => prefix).join('');
package/esm/template.js CHANGED
@@ -260,7 +260,9 @@ export class Template {
260
260
  if (link[0] === '/')
261
261
  return link;
262
262
  const result = normalize(this.filePath + sep + link).substring(appDir.length);
263
- return (sep === '/') ? result : result.replaceAll(sep, '/');
263
+ return (((sep === '/') ? result : result.replaceAll(sep, '/')))
264
+ .replace(/^\/node_modules\/@itrocks\//, '/@itrocks/')
265
+ .replace(/^\/node_modules\//, '/lib/');
264
266
  }
265
267
  async parseBuffer(buffer) {
266
268
  this.prefixes = this.parsers.map(([prefix]) => prefix).join('');
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.1.7"
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