@kosatyi/ejs 0.0.97 → 0.0.99

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/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import fs from 'fs';
2
- import path from 'path';
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
3
 
4
4
  const typeProp = function () {
5
5
  const args = [].slice.call(arguments);
@@ -60,13 +60,22 @@ const symbols = (string) => {
60
60
 
61
61
  const safeValue = (value, escape) => {
62
62
  const check = value;
63
- return check == null ? '' : Boolean(escape) === true ? entities(check) : check
63
+ return check == null
64
+ ? ''
65
+ : Boolean(escape) === true
66
+ ? entities(check)
67
+ : check
64
68
  };
65
69
 
66
70
  const instanceOf = (object, instance) => {
67
71
  return Boolean(object instanceof instance)
68
72
  };
69
73
 
74
+ const assertInstanceOf = (object, instance) => {
75
+ if (instanceOf(object, instance) === false)
76
+ throw new TypeError(`${object} in not instance of ${instance}`)
77
+ };
78
+
70
79
  const getPath = (context, name, strict) => {
71
80
  let data = context;
72
81
  let chunks = String(name).split('.');
@@ -88,6 +97,14 @@ const getPath = (context, name, strict) => {
88
97
  return [data, prop]
89
98
  };
90
99
 
100
+ const bindContext = (object, methods = []) => {
101
+ methods.forEach((name) => {
102
+ if (name in object) {
103
+ object[name] = object[name].bind(object);
104
+ }
105
+ });
106
+ };
107
+
91
108
  const ext = (path, defaults) => {
92
109
  const ext = path.split('.').pop();
93
110
  if (ext !== defaults) {
@@ -155,6 +172,12 @@ const omit = (object, list) => {
155
172
  })
156
173
  };
157
174
 
175
+ /**
176
+ *
177
+ * @param object
178
+ * @param prop
179
+ * @return {boolean}
180
+ */
158
181
  const hasProp = (object, prop) => {
159
182
  return object && object.hasOwnProperty(prop)
160
183
  };
@@ -165,6 +188,18 @@ const joinPath = (path, template) => {
165
188
  return template
166
189
  };
167
190
 
191
+ const matchTokens = (regex, text, callback) => {
192
+ let index = 0;
193
+ text.replace(regex, function () {
194
+ const params = [].slice.call(arguments, 0, -1);
195
+ const offset = params.pop();
196
+ const match = params.shift();
197
+ callback(params, index, offset);
198
+ index = offset + match.length;
199
+ return match
200
+ });
201
+ };
202
+
168
203
  const defaults = {};
169
204
 
170
205
  defaults.export = 'ejsPrecompiled';
@@ -183,6 +218,7 @@ defaults.globalHelpers = [];
183
218
  defaults.vars = {
184
219
  SCOPE: 'ejs',
185
220
  COMPONENT: 'ui',
221
+ ELEMENT: 'el',
186
222
  EXTEND: '$$e',
187
223
  BUFFER: '$$a',
188
224
  LAYOUT: '$$l',
@@ -243,159 +279,146 @@ const configSchema = (config, options) => {
243
279
 
244
280
  const global = typeof globalThis !== 'undefined' ? globalThis : window || self;
245
281
 
246
- function Cache(config) {
247
- if (instanceOf(this, Cache) === false) return new Cache()
248
- const cache = {
249
- enabled: true,
250
- list: {},
251
- };
252
- this.configure = function (config) {
253
- cache.enabled = config.cache;
254
- if (isNode() === false) {
255
- this.load(global[config.export]);
256
- }
257
- };
258
- this.clear = function () {
259
- cache.list = {};
260
- };
261
- this.load = function (data) {
262
- if (cache.enabled) {
263
- extend(cache.list, data || {});
282
+ class Cache {
283
+ #enabled = true
284
+ #list = {}
285
+ constructor(config) {
286
+ this.configure(config);
287
+ }
288
+ load(data) {
289
+ if (this.#enabled) {
290
+ extend(this.#list, data || {});
264
291
  }
265
- return this
266
- };
267
- this.get = function (key) {
268
- if (cache.enabled) {
269
- return cache.list[key]
292
+ }
293
+ get(key) {
294
+ if (this.#enabled) {
295
+ return this.#list[key]
270
296
  }
271
- };
272
- this.set = function (key, value) {
273
- if (cache.enabled) {
274
- cache.list[key] = value;
297
+ }
298
+ set(key, value) {
299
+ if (this.#enabled) {
300
+ this.#list[key] = value;
275
301
  }
276
- return this
277
- };
278
- this.resolve = function (key) {
302
+ }
303
+ exist(key) {
304
+ return hasProp(this.#list, key)
305
+ }
306
+ clear() {
307
+ this.#list = {};
308
+ }
309
+ remove(key) {
310
+ delete this.#list[key];
311
+ }
312
+ resolve(key) {
279
313
  return Promise.resolve(this.get(key))
280
- };
281
- this.remove = function (key) {
282
- delete cache.list[key];
283
- };
284
- this.exist = function (key) {
285
- return hasProp(cache.list, key)
286
- };
314
+ }
315
+ configure(config) {
316
+ this.#enabled = config.cache;
317
+ if (isNode() === false) {
318
+ this.load(global[config.export]);
319
+ }
320
+ }
287
321
  }
288
322
 
289
- const tagList = [
290
- {
291
- symbol: '-',
292
- format(value) {
293
- return `')\n${this.BUFFER}(${this.SAFE}(${value},1))\n${this.BUFFER}('`
323
+ class Compiler {
324
+ #config = {}
325
+ #symbols = [
326
+ {
327
+ symbol: '-',
328
+ format(value) {
329
+ return `')\n${this.BUFFER}(${this.SAFE}(${value},1))\n${this.BUFFER}('`
330
+ },
294
331
  },
295
- },
296
- {
297
- symbol: '=',
298
- format(value) {
299
- return `')\n${this.BUFFER}(${this.SAFE}(${value}))\n${this.BUFFER}('`
332
+ {
333
+ symbol: '=',
334
+ format(value) {
335
+ return `')\n${this.BUFFER}(${this.SAFE}(${value}))\n${this.BUFFER}('`
336
+ },
300
337
  },
301
- },
302
- {
303
- symbol: '#',
304
- format(value) {
305
- return `')\n/**${value}**/\n${this.BUFFER}('`
338
+ {
339
+ symbol: '#',
340
+ format(value) {
341
+ return `')\n/**${value}**/\n${this.BUFFER}('`
342
+ },
306
343
  },
307
- },
308
- {
309
- symbol: '',
310
- format(value) {
311
- return `')\n${value.trim()}\n${this.BUFFER}('`
344
+ {
345
+ symbol: '',
346
+ format(value) {
347
+ return `')\n${value.trim()}\n${this.BUFFER}('`
348
+ },
312
349
  },
313
- },
314
- ];
315
-
316
- function matchTokens(regex, text, callback) {
317
- let index = 0;
318
- text.replace(regex, function () {
319
- const params = [].slice.call(arguments, 0, -1);
320
- const offset = params.pop();
321
- const match = params.shift();
322
- callback(params, index, offset);
323
- index = offset + match.length;
324
- return match
325
- });
326
- }
327
-
328
- function Compiler(config) {
329
- if (instanceOf(this, Compiler) === false) return new Compiler(config)
330
-
331
- const compiler = {};
332
-
333
- this.configure = function (config) {
334
- compiler.withObject = config.withObject;
335
- compiler.rmWhitespace = config.rmWhitespace;
336
- compiler.token = config.token;
337
- compiler.vars = config.vars;
338
- compiler.globalHelpers = config.globalHelpers;
339
- compiler.matches = [];
340
- compiler.formats = [];
341
- compiler.slurp = {
342
- match: '[\s\t\n]*',
343
- start: [compiler.token.start, '_'],
344
- end: ['_', compiler.token.end],
350
+ ]
351
+ constructor(config) {
352
+ this.configure(config);
353
+ }
354
+ configure(config) {
355
+ this.#config.withObject = config.withObject;
356
+ this.#config.rmWhitespace = config.rmWhitespace;
357
+ this.#config.token = config.token;
358
+ this.#config.vars = config.vars;
359
+ this.#config.globalHelpers = config.globalHelpers;
360
+ this.#config.matches = [];
361
+ this.#config.formats = [];
362
+ this.#config.slurp = {
363
+ match: '[s\t\n]*',
364
+ start: [this.#config.token.start, '_'],
365
+ end: ['_', this.#config.token.end],
345
366
  };
346
- tagList.forEach((item) => {
347
- compiler.matches.push(
348
- compiler.token.start
367
+ this.#symbols.forEach((item) => {
368
+ this.#config.matches.push(
369
+ this.#config.token.start
349
370
  .concat(item.symbol)
350
- .concat(compiler.token.regex)
351
- .concat(compiler.token.end)
371
+ .concat(this.#config.token.regex)
372
+ .concat(this.#config.token.end)
352
373
  );
353
- compiler.formats.push(item.format.bind(compiler.vars));
374
+ this.#config.formats.push(item.format.bind(this.#config.vars));
354
375
  });
355
- compiler.regex = new RegExp(
356
- compiler.matches.join('|').concat('|$'),
376
+ this.#config.regex = new RegExp(
377
+ this.#config.matches.join('|').concat('|$'),
357
378
  'g'
358
379
  );
359
- compiler.slurpStart = new RegExp(
360
- [compiler.slurp.match, compiler.slurp.start.join('')].join(''),
380
+ this.#config.slurpStart = new RegExp(
381
+ [this.#config.slurp.match, this.#config.slurp.start.join('')].join(
382
+ ''
383
+ ),
361
384
  'gm'
362
385
  );
363
- compiler.slurpEnd = new RegExp(
364
- [compiler.slurp.end.join(''), compiler.slurp.match].join(''),
386
+ this.#config.slurpEnd = new RegExp(
387
+ [this.#config.slurp.end.join(''), this.#config.slurp.match].join(
388
+ ''
389
+ ),
365
390
  'gm'
366
391
  );
367
- };
368
-
369
- this.compile = function (content, path) {
370
- const { SCOPE, SAFE, BUFFER, COMPONENT } = compiler.vars;
371
- const GLOBALS = compiler.globalHelpers;
372
- content = String(content);
373
- if (compiler.rmWhitespace) {
374
- content = content
392
+ }
393
+ compile(content, path) {
394
+ const { SCOPE, SAFE, BUFFER, COMPONENT, ELEMENT } = this.#config.vars;
395
+ const GLOBALS = this.#config.globalHelpers;
396
+ if (this.#config.rmWhitespace) {
397
+ content = String(content)
375
398
  .replace(/[\r\n]+/g, '\n')
376
399
  .replace(/^\s+|\s+$/gm, '');
377
400
  }
378
- content = content
379
- .replace(compiler.slurpStart, compiler.token.start)
380
- .replace(compiler.slurpEnd, compiler.token.end);
401
+ content = String(content)
402
+ .replace(this.#config.slurpStart, this.#config.token.start)
403
+ .replace(this.#config.slurpEnd, this.#config.token.end);
381
404
  let source = `${BUFFER}('`;
382
- matchTokens(compiler.regex, content, (params, index, offset) => {
405
+ matchTokens(this.#config.regex, content, (params, index, offset) => {
383
406
  source += symbols(content.slice(index, offset));
384
407
  params.forEach((value, index) => {
385
408
  if (value) {
386
- source += compiler.formats[index](value);
409
+ source += this.#config.formats[index](value);
387
410
  }
388
411
  });
389
412
  });
390
413
  source += `');`;
391
414
  source = `try{${source}}catch(e){return ${BUFFER}.error(e)}`;
392
- if (compiler.withObject) {
415
+ if (this.#config.withObject) {
393
416
  source = `with(${SCOPE}){${source}}`;
394
417
  }
395
418
  source = `${BUFFER}.start();${source}return ${BUFFER}.end();`;
396
419
  source += `\n//# sourceURL=${path}`;
397
420
  let result = null;
398
- let params = [SCOPE, COMPONENT, BUFFER, SAFE].concat(GLOBALS);
421
+ let params = [SCOPE, BUFFER, SAFE, COMPONENT, ELEMENT].concat(GLOBALS);
399
422
  try {
400
423
  result = Function.apply(null, params.concat(source));
401
424
  result.source = `(function(${params.join(',')}){\n${source}\n});`;
@@ -405,57 +428,49 @@ function Compiler(config) {
405
428
  throw e
406
429
  }
407
430
  return result
408
- };
409
-
410
- this.configure(config);
431
+ }
411
432
  }
412
433
 
413
- function Template(config, cache, compiler) {
414
- if (instanceOf(this, Template) === false)
415
- return new Template(config, cache, compiler)
416
-
417
- if (instanceOf(cache, Cache) === false)
418
- throw new TypeError('cache is not instance of Cache')
419
-
420
- if (instanceOf(compiler, Compiler) === false)
421
- throw new TypeError('compiler is not instance of Compiler')
422
-
423
- const template = {};
424
-
425
- const result = (template, content) => {
426
- cache.set(template, content);
434
+ class Template {
435
+ #path
436
+ #cache
437
+ #compiler
438
+ #resolver
439
+ constructor(config, cache, compiler) {
440
+ assertInstanceOf(cache, Cache);
441
+ assertInstanceOf(compiler, Compiler);
442
+ this.#cache = cache;
443
+ this.#compiler = compiler;
444
+ this.configure(config);
445
+ }
446
+ #resolve(path) {
447
+ return this.#resolver(this.#path, path)
448
+ }
449
+ #result(template, content) {
450
+ this.#cache.set(template, content);
427
451
  return content
428
- };
429
-
430
- const resolve = (path) => {
431
- return template.resolver(template.path, path)
432
- };
433
-
434
- const compile = (content, template) => {
452
+ }
453
+ #compile(content, template) {
435
454
  if (isFunction(content)) {
436
455
  return content
437
456
  } else {
438
- return compiler.compile(content, template)
457
+ return this.#compiler.compile(content, template)
439
458
  }
440
- };
441
-
442
- this.configure = function (config) {
443
- template.path = config.path;
444
- template.cache = config.cache;
459
+ }
460
+ configure(config) {
461
+ this.#path = config.path;
445
462
  if (isFunction(config.resolver)) {
446
- template.resolver = config.resolver;
463
+ this.#resolver = config.resolver;
447
464
  }
448
- };
449
-
450
- this.get = function (template) {
451
- if (cache.exist(template)) {
452
- return cache.resolve(template)
465
+ }
466
+ get(template) {
467
+ if (this.#cache.exist(template)) {
468
+ return this.#cache.resolve(template)
453
469
  }
454
- return resolve(template).then((content) =>
455
- result(template, compile(content, template))
470
+ return this.#resolve(template).then((content) =>
471
+ this.#result(template, this.#compile(content, template))
456
472
  )
457
- };
458
- this.configure(config);
473
+ }
459
474
  }
460
475
 
461
476
  const selfClosed = [
@@ -503,537 +518,462 @@ const element = (tag, attrs, content) => {
503
518
  return result.join('')
504
519
  };
505
520
 
506
- /**
507
- * @extends Error
508
- * @property code
509
- * @param {string} message
510
- * @constructor
511
- */
512
- function TemplateError(message) {
513
- this.name = 'TemplateError';
514
- this.message = message;
515
- Error.call(this);
521
+ class TemplateError extends Error {
522
+ code = 0
523
+ constructor(message) {
524
+ super();
525
+ this.message = message;
526
+ }
527
+ getCode() {
528
+ return this.code
529
+ }
530
+ getMessage() {
531
+ return this.message
532
+ }
533
+ toString() {
534
+ return this.getMessage()
535
+ }
516
536
  }
517
537
 
518
- /**
519
- *
520
- */
521
- Object.setPrototypeOf(TemplateError.prototype, Error.prototype);
522
- Object.assign(TemplateError.prototype, { code: 1 });
523
- /**
524
- *
525
- * @return {number}
526
- */
527
- TemplateError.prototype.getCode = function() {
528
- return this.code
529
- };
530
- /**
531
- *
532
- * @return {string}
533
- */
534
- TemplateError.prototype.getMessage = function() {
535
- return this.message
536
- };
537
- /**
538
- * @return {string}
539
- */
540
- TemplateError.prototype.toString = function() {
541
- return this.getMessage()
542
- };
543
-
544
- /**
545
- * @extends TemplateError
546
- * @param {string} message
547
- * @constructor
548
- */
549
- function TemplateNotFound(message) {
550
- TemplateError.call(this);
551
- this.name = 'TemplateNotFound';
552
- this.message = message;
538
+ class TemplateNotFound extends TemplateError {
539
+ code = 404
553
540
  }
554
541
 
555
- /**
556
- *
557
- */
558
- Object.setPrototypeOf(TemplateNotFound.prototype, TemplateError.prototype);
559
- Object.assign(TemplateNotFound.prototype, { code: 404 });
560
- /**
561
- * @extends TemplateError
562
- * @param {string} message
563
- * @constructor
564
- */
565
- function TemplateSyntaxError(message) {
566
- TemplateError.call(this);
567
- this.name = 'TemplateSyntaxError';
568
- this.message = message;
542
+ class TemplateSyntaxError extends TemplateError {
543
+ code = 500
569
544
  }
570
545
 
571
- /**
572
- *
573
- */
574
- Object.setPrototypeOf(TemplateSyntaxError.prototype, TemplateError.prototype);
575
- Object.assign(TemplateSyntaxError.prototype, { code: 500 });
576
-
577
546
  function resolve(list) {
578
- return Promise.all(list || []).then((list) => list.join('')).catch((e) => e)
547
+ return Promise.all(list || [])
548
+ .then((list) => list.join(''))
549
+ .catch((e) => e)
579
550
  }
580
551
 
581
552
  function reject(error) {
582
553
  return Promise.reject(new TemplateSyntaxError(error.message))
583
554
  }
584
555
 
556
+ /**
557
+ *
558
+ * @return {buffer}
559
+ */
585
560
  function createBuffer() {
586
561
  let store = [],
587
562
  array = [];
588
563
 
589
- function buffer(value) {
564
+ const buffer = (value) => {
590
565
  array.push(value);
591
- }
566
+ };
592
567
 
593
- buffer.start = function() {
568
+ buffer.start = () => {
594
569
  array = [];
595
570
  };
596
- buffer.backup = function() {
571
+ buffer.backup = () => {
597
572
  store.push(array.concat());
598
573
  array = [];
599
574
  };
600
- buffer.restore = function() {
575
+ buffer.restore = () => {
601
576
  const result = array.concat();
602
577
  array = store.pop();
603
578
  return resolve(result)
604
579
  };
605
- buffer.error = function(e) {
580
+ buffer.error = (e) => {
606
581
  return reject(e)
607
582
  };
608
- buffer.end = function() {
583
+ buffer.end = () => {
609
584
  return resolve(array)
610
585
  };
611
586
  return buffer
612
587
  }
613
588
 
614
- function Context(config) {
615
- if (instanceOf(this, Context) === false) return new Context(config)
616
- this.configure = function (config, methods) {
617
- const { BLOCKS, MACRO, EXTEND, LAYOUT, BUFFER, COMPONENT } = config.vars;
618
-
619
- this.create = function (data) {
620
- return new Scope(data)
621
- };
622
-
623
- this.helpers = function (methods) {
624
- extend(Scope.prototype, methods || {});
625
- };
626
- /**
627
- * @name ContextScope
628
- * @param data
629
- * @constructor
630
- */
631
- function Scope(data) {
632
- this[BLOCKS] = {};
633
- this[MACRO] = {};
634
- extend(this, data || {});
635
- }
636
-
637
- Scope.prototype = extend({}, methods || {});
638
- Object.defineProperties(Scope.prototype, {
639
- [BUFFER]: {
640
- value: createBuffer(),
641
- writable: true,
642
- configurable: false,
643
- enumerable: false,
644
- },
645
- [BLOCKS]: {
646
- value: {},
647
- writable: true,
648
- configurable: false,
649
- enumerable: false,
650
- },
651
- [MACRO]: {
652
- value: {},
653
- writable: true,
654
- configurable: false,
655
- enumerable: false,
656
- },
657
- [LAYOUT]: {
658
- value: false,
659
- writable: true,
660
- configurable: false,
661
- enumerable: false,
662
- },
663
- [EXTEND]: {
664
- value: false,
665
- writable: true,
666
- configurable: false,
667
- enumerable: false,
589
+ const createContextScope = (config, methods) => {
590
+ const {
591
+ BLOCKS,
592
+ MACRO,
593
+ EXTEND,
594
+ LAYOUT,
595
+ BUFFER,
596
+ SAFE,
597
+ SCOPE,
598
+ COMPONENT,
599
+ ELEMENT,
600
+ } = config.vars;
601
+ /**
602
+ * @name ContextScope
603
+ * @param data
604
+ * @constructor
605
+ */
606
+ function ContextScope(data) {
607
+ this[BLOCKS] = {};
608
+ this[MACRO] = {};
609
+ Object.assign(
610
+ this,
611
+ omit(data, [SCOPE, BUFFER, SAFE, COMPONENT, ELEMENT])
612
+ );
613
+ }
614
+ Object.assign(ContextScope.prototype, methods);
615
+ Object.defineProperties(ContextScope.prototype, {
616
+ [BUFFER]: {
617
+ value: createBuffer(),
618
+ },
619
+ [BLOCKS]: {
620
+ value: {},
621
+ writable: true,
622
+ },
623
+ [MACRO]: {
624
+ value: {},
625
+ writable: true,
626
+ },
627
+ [LAYOUT]: {
628
+ value: false,
629
+ writable: true,
630
+ },
631
+ [EXTEND]: {
632
+ value: false,
633
+ writable: true,
634
+ },
635
+ /** @type {function} */
636
+ useSafeValue: {
637
+ get: () => safeValue,
638
+ },
639
+ /** @type {function} */
640
+ useComponent: {
641
+ get() {
642
+ if (isFunction(this[COMPONENT])) {
643
+ return this[COMPONENT].bind(this)
644
+ } else {
645
+ return () => {
646
+ throw new Error(`${COMPONENT} must be a function`)
647
+ }
648
+ }
668
649
  },
669
- getMacro: {
670
- value() {
671
- return this[MACRO]
672
- },
673
- writable: false,
674
- configurable: false,
675
- enumerable: false,
650
+ },
651
+ /** @type {function} */
652
+ useElement: {
653
+ get() {
654
+ if (isFunction(this[ELEMENT])) {
655
+ return this[ELEMENT].bind(this)
656
+ } else {
657
+ return () => {
658
+ throw new Error(`${ELEMENT} must be a function`)
659
+ }
660
+ }
676
661
  },
677
- getBuffer: {
678
- value() {
679
- return this[BUFFER]
680
- },
681
- writable: false,
682
- configurable: false,
683
- enumerable: false,
662
+ },
663
+ /** @type {()=>this[MACRO]} */
664
+ getMacro: {
665
+ value() {
666
+ return this[MACRO]
684
667
  },
685
- getComponent: {
686
- value() {
687
- const context = this;
688
- if (COMPONENT in context) {
689
- return function () {
690
- return context[COMPONENT].apply(context, arguments)
691
- }
692
- }
693
- return function () {
694
- console.log('%s function not defined', COMPONENT);
695
- }
696
- },
697
- writable: false,
698
- configurable: false,
699
- enumerable: false,
668
+ },
669
+ /** @type {function} */
670
+ getBuffer: {
671
+ value() {
672
+ return this[BUFFER]
700
673
  },
701
- getBlocks: {
702
- value() {
703
- return this[BLOCKS]
704
- },
705
- writable: false,
706
- configurable: false,
707
- enumerable: false,
674
+ },
675
+ /** @type {function} */
676
+ getBlocks: {
677
+ value() {
678
+ return this[BLOCKS]
708
679
  },
709
- setExtend: {
710
- value(value) {
711
- this[EXTEND] = value;
712
- },
713
- writable: false,
714
- configurable: false,
715
- enumerable: false,
680
+ },
681
+ /** @type {function} */
682
+ setExtend: {
683
+ value(value) {
684
+ this[EXTEND] = value;
716
685
  },
717
- getExtend: {
718
- value() {
719
- return this[EXTEND]
720
- },
721
- writable: false,
722
- configurable: false,
723
- enumerable: false,
686
+ },
687
+ /** @type {function} */
688
+ getExtend: {
689
+ value() {
690
+ return this[EXTEND]
724
691
  },
725
- setLayout: {
726
- value(layout) {
727
- this[LAYOUT] = layout;
728
- },
729
- writable: false,
730
- configurable: false,
731
- enumerable: false,
692
+ },
693
+ /** @type {function} */
694
+ setLayout: {
695
+ value(layout) {
696
+ this[LAYOUT] = layout;
732
697
  },
733
- getLayout: {
734
- value() {
735
- return this[LAYOUT]
736
- },
737
- writable: false,
738
- configurable: false,
739
- enumerable: false,
698
+ },
699
+ /** @type {function} */
700
+ getLayout: {
701
+ value() {
702
+ return this[LAYOUT]
740
703
  },
741
- clone: {
742
- value(exclude_blocks) {
743
- const filter = [LAYOUT, EXTEND, BUFFER];
744
- if (exclude_blocks === true) {
745
- filter.push(BLOCKS);
746
- }
747
- return omit(this, filter)
748
- },
749
- writable: false,
750
- configurable: false,
751
- enumerable: false,
704
+ },
705
+ /** @type {function} */
706
+ clone: {
707
+ value(exclude_blocks) {
708
+ const filter = [LAYOUT, EXTEND, BUFFER];
709
+ if (exclude_blocks === true) {
710
+ filter.push(BLOCKS);
711
+ }
712
+ return omit(this, filter)
752
713
  },
753
- extend: {
754
- value(layout) {
755
- this.setExtend(true);
756
- this.setLayout(layout);
757
- },
758
- writable: false,
759
- configurable: false,
760
- enumerable: false,
714
+ },
715
+ /** @type {function} */
716
+ extend: {
717
+ value(layout) {
718
+ this.setExtend(true);
719
+ this.setLayout(layout);
761
720
  },
762
- echo: {
763
- value(layout) {
764
- const buffer = this.getBuffer();
765
- const params = [].slice.call(arguments);
766
- params.forEach(buffer);
767
- },
768
- writable: false,
769
- configurable: false,
770
- enumerable: false,
721
+ },
722
+ /** @type {function} */
723
+ echo: {
724
+ value(layout) {
725
+ const buffer = this.getBuffer();
726
+ const params = [].slice.call(arguments);
727
+ params.forEach(buffer);
771
728
  },
772
- fn: {
773
- value(callback) {
774
- const buffer = this.getBuffer();
775
- const context = this;
776
- return function () {
729
+ },
730
+ /** @type {function} */
731
+ fn: {
732
+ value(callback) {
733
+ const buffer = this.getBuffer();
734
+ const context = this;
735
+ return function () {
736
+ if (isFunction(callback)) {
777
737
  buffer.backup();
778
- if (isFunction(callback)) {
779
- callback.apply(context, arguments);
780
- }
738
+ buffer(callback.apply(context, arguments));
781
739
  return buffer.restore()
782
740
  }
783
- },
784
- writable: false,
785
- configurable: false,
786
- enumerable: false,
787
- },
788
- get: {
789
- value(name, defaults) {
790
- const path = getPath(this, name, true);
791
- const result = path.shift();
792
- const prop = path.pop();
793
- return hasProp(result, prop) ? result[prop] : defaults
794
- },
795
- writable: true,
796
- configurable: true,
797
- enumerable: false,
798
- },
799
- set: {
800
- value(name, value) {
801
- const path = getPath(this, name, false);
802
- const result = path.shift();
803
- const prop = path.pop();
804
- if (this.getExtend() && hasProp(result, prop)) {
805
- return result[prop]
806
- }
807
- return (result[prop] = value)
808
- },
809
- writable: false,
810
- configurable: false,
811
- enumerable: false,
741
+ }
812
742
  },
813
- macro: {
814
- value(name, callback) {
815
- const list = this.getMacro();
816
- const macro = this.fn(callback);
817
- const context = this;
818
- list[name] = function () {
819
- return context.echo(macro.apply(undefined, arguments))
820
- };
821
- },
822
- writable: false,
823
- configurable: false,
824
- enumerable: false,
743
+ },
744
+ /** @type {function} */
745
+ macro: {
746
+ value(name, callback) {
747
+ const list = this.getMacro();
748
+ const macro = this.fn(callback);
749
+ const context = this;
750
+ list[name] = function () {
751
+ return context.echo(macro.apply(undefined, arguments))
752
+ };
825
753
  },
826
- call: {
827
- value(name) {
828
- const list = this.getMacro();
829
- const macro = list[name];
830
- const params = [].slice.call(arguments, 1);
831
- if (isFunction(macro)) {
832
- return macro.apply(macro, params)
833
- }
834
- },
835
- writable: false,
836
- configurable: false,
837
- enumerable: false,
754
+ },
755
+ /** @type {function} */
756
+ call: {
757
+ value(name) {
758
+ const list = this.getMacro();
759
+ const macro = list[name];
760
+ const params = [].slice.call(arguments, 1);
761
+ if (isFunction(macro)) {
762
+ return macro.apply(macro, params)
763
+ }
838
764
  },
839
- block: {
840
- value(name, callback) {
841
- const blocks = this.getBlocks();
842
- blocks[name] = blocks[name] || [];
843
- blocks[name].push(this.fn(callback));
844
- if (this.getExtend()) return
845
- const list = Object.assign([], blocks[name]);
846
- const current = function () {
847
- return list.shift()
848
- };
849
- const next = () => {
850
- const parent = current();
851
- if (parent) {
852
- return () => {
853
- this.echo(parent(next()));
854
- }
855
- } else {
856
- return noop
765
+ },
766
+ /** @type {function} */
767
+ block: {
768
+ value(name, callback) {
769
+ const blocks = this.getBlocks();
770
+ blocks[name] = blocks[name] || [];
771
+ blocks[name].push(this.fn(callback));
772
+ if (this.getExtend()) return
773
+ const list = Object.assign([], blocks[name]);
774
+ const current = () => {
775
+ return list.shift()
776
+ };
777
+ const next = () => {
778
+ const parent = current();
779
+ if (parent) {
780
+ return () => {
781
+ this.echo(parent(next()));
857
782
  }
858
- };
859
- this.echo(current()(next()));
860
- },
861
- writable: false,
862
- configurable: false,
863
- enumerable: false,
783
+ } else {
784
+ return noop
785
+ }
786
+ };
787
+ this.echo(current()(next()));
864
788
  },
865
- hasBlock: {
866
- value(name) {
867
- return this.getBlocks().hasOwnProperty(name)
868
- },
869
- writable: false,
870
- configurable: false,
871
- enumerable: false,
789
+ },
790
+ /** @type {function} */
791
+ hasBlock: {
792
+ value(name) {
793
+ return this.getBlocks().hasOwnProperty(name)
872
794
  },
873
- include: {
874
- value(path, data, cx) {
875
- const context = cx === false ? {} : this.clone(true);
876
- const params = extend(context, data || {});
877
- const promise = this.render(path, params);
878
- this.echo(promise);
879
- },
880
- writable: false,
881
- configurable: false,
882
- enumerable: false,
795
+ },
796
+ /** @type {function} */
797
+ include: {
798
+ value(path, data, cx) {
799
+ const context = cx === false ? {} : this.clone(true);
800
+ const params = extend(context, data || {});
801
+ const promise = this.render(path, params);
802
+ this.echo(promise);
883
803
  },
884
- promiseResolve: {
885
- value(value, callback) {
886
- return Promise.resolve(
887
- isFunction(value) ? this.fn(value)() : value
888
- ).then(callback.bind(this))
889
- },
890
- writable: false,
891
- configurable: false,
892
- enumerable: false,
804
+ },
805
+ /** @type {function} */
806
+ use: {
807
+ value(path, namespace) {
808
+ this.echo(
809
+ Promise.resolve(this.require(path)).then((exports) => {
810
+ const list = this.getMacro();
811
+ each(exports, function (macro, name) {
812
+ list[[namespace, name].join('.')] = macro;
813
+ });
814
+ })
815
+ );
893
816
  },
894
- use: {
895
- value(path, namespace) {
896
- this.echo(
897
- this.promiseResolve(
898
- this.require(path),
899
- function (exports) {
900
- const list = this.getMacro();
901
- each(exports, function (macro, name) {
902
- list[[namespace, name].join('.')] = macro;
903
- });
904
- }
905
- )
906
- );
907
- },
908
- writable: false,
909
- configurable: false,
910
- enumerable: false,
817
+ },
818
+ /** @type {function} */
819
+ async: {
820
+ value(promise, callback) {
821
+ this.echo(Promise.resolve(promise).then(callback));
911
822
  },
912
- async: {
913
- value(promise, callback) {
914
- this.echo(
915
- this.promiseResolve(promise, function (data) {
916
- return this.fn(callback)(data)
917
- })
918
- );
919
- },
920
- writable: false,
921
- configurable: false,
922
- enumerable: false,
823
+ },
824
+ /** @type {function} */
825
+ get: {
826
+ value(name, defaults) {
827
+ const path = getPath(this, name, true);
828
+ const result = path.shift();
829
+ const prop = path.pop();
830
+ return hasProp(result, prop) ? result[prop] : defaults
923
831
  },
924
- each: {
925
- value: function (object, callback) {
926
- if (isString(object)) {
927
- object = this.get(object, []);
928
- }
929
- each(object, callback);
930
- },
931
- writable: false,
932
- configurable: false,
933
- enumerable: false,
832
+ },
833
+ /** @type {function} */
834
+ set: {
835
+ value(name, value) {
836
+ const path = getPath(this, name, false);
837
+ const result = path.shift();
838
+ const prop = path.pop();
839
+ if (this.getExtend() && hasProp(result, prop)) {
840
+ return result[prop]
841
+ }
842
+ return (result[prop] = value)
934
843
  },
935
- element: {
936
- value(tag, attr, content) {
937
- return element(tag, attr, content)
938
- },
939
- writable: false,
940
- configurable: false,
941
- enumerable: false,
844
+ },
845
+ /** @type {function} */
846
+ each: {
847
+ value: function (object, callback) {
848
+ if (isString(object)) {
849
+ object = this.get(object, []);
850
+ }
851
+ each(object, callback);
942
852
  },
943
- el: {
944
- value(tag, attr, content) {
945
- this.echo(
946
- this.promiseResolve(content, function (content) {
947
- return this.element(tag, attr, content)
948
- })
949
- );
950
- },
951
- writable: false,
952
- configurable: false,
953
- enumerable: false,
853
+ writable: true,
854
+ },
855
+ /** @type {function} */
856
+ el: {
857
+ value(tag, attr, content) {
858
+ content = isFunction(content) ? this.fn(content)() : content;
859
+ this.echo(
860
+ Promise.resolve(content).then((content) =>
861
+ element(tag, attr, content)
862
+ )
863
+ );
954
864
  },
955
- });
956
- };
957
- this.configure(config);
958
- }
959
-
960
- function EJS(options) {
961
- if (instanceOf(this, EJS) === false) return new EJS(options)
962
-
963
- const scope = {};
964
- const config = {};
965
-
966
- configSchema(config, options || {});
865
+ writable: true,
866
+ },
867
+ /** @type {function} */
868
+ ui: {
869
+ value(layout) {},
870
+ writable: true,
871
+ },
872
+ });
873
+ return ContextScope
874
+ };
967
875
 
968
- const context = new Context(config);
969
- const compiler = new Compiler(config);
970
- const cache = new Cache();
971
- const template = new Template(config, cache, compiler);
876
+ class Context {
877
+ #scope
878
+ constructor(config, methods) {
879
+ this.configure(config, methods);
880
+ }
881
+ create(data) {
882
+ return new this.#scope(data)
883
+ }
884
+ configure(config, methods) {
885
+ this.#scope = createContextScope(config, methods);
886
+ }
887
+ helpers(methods) {
888
+ extend(this.#scope.prototype, methods || {});
889
+ }
890
+ }
972
891
 
973
- const output = (path, scope) => {
974
- const { globalHelpers } = config;
892
+ class EJS {
893
+ #config = {}
894
+ #extend = {}
895
+ #context
896
+ #compiler
897
+ #cache
898
+ #template
899
+ constructor(options) {
900
+ configSchema(this.#config, options || {});
901
+ this.#context = new Context(this.#config, this.#extend);
902
+ this.#compiler = new Compiler(this.#config);
903
+ this.#cache = new Cache(this.#config);
904
+ this.#template = new Template(this.#config, this.#cache, this.#compiler);
905
+ //
906
+ bindContext(this, [
907
+ 'configure',
908
+ 'create',
909
+ 'render',
910
+ 'require',
911
+ 'context',
912
+ 'preload',
913
+ 'compile',
914
+ 'helpers',
915
+ ]);
916
+ //
917
+ this.helpers({ require: this.require, render: this.render });
918
+ }
919
+ configure(options) {
920
+ configSchema(this.#config, options || {});
921
+ this.#context.configure(this.#config, this.#extend);
922
+ this.#compiler.configure(this.#config);
923
+ this.#cache.configure(this.#config);
924
+ this.#template.configure(this.#config);
925
+ return this.#config
926
+ }
927
+ render(name, data) {
928
+ const filepath = ext(name, this.#config.extension);
929
+ const scope = this.context(data);
930
+ return this.#output(filepath, scope).then((content) => {
931
+ if (scope.getExtend()) {
932
+ scope.setExtend(false);
933
+ const layout = scope.getLayout();
934
+ const data = scope.clone();
935
+ return this.render(layout, data)
936
+ }
937
+ return content
938
+ })
939
+ }
940
+ helpers(methods) {
941
+ this.#context.helpers(extend(this.#extend, methods));
942
+ }
943
+ context(data) {
944
+ return this.#context.create(data)
945
+ }
946
+ compile(content, path) {
947
+ return this.#compiler.compile(content, path)
948
+ }
949
+ preload(list) {
950
+ return this.#cache.load(list || {})
951
+ }
952
+ create(options) {
953
+ return new this.constructor(options)
954
+ }
955
+ require(name) {
956
+ const filepath = ext(name, this.#config.extension);
957
+ const scope = this.context({});
958
+ return this.#output(filepath, scope).then(() => scope.getMacro())
959
+ }
960
+ #output(path, scope) {
961
+ const { globalHelpers } = this.#config;
975
962
  const params = [
976
963
  scope,
977
- scope.getComponent(),
978
964
  scope.getBuffer(),
979
- safeValue,
965
+ scope.useSafeValue,
966
+ scope.useComponent,
967
+ scope.useElement,
980
968
  ].concat(
981
969
  globalHelpers
982
970
  .filter((name) => isFunction(scope[name]))
983
971
  .map((name) => scope[name].bind(scope))
984
972
  );
985
- return template
973
+ return this.#template
986
974
  .get(path)
987
975
  .then((callback) => callback.apply(scope, params))
988
- };
989
-
990
- const require = (name) => {
991
- const filepath = ext(name, config.extension);
992
- const scope = context.create({});
993
- return output(filepath, scope).then(() => scope.getMacro())
994
- };
995
- const render = (name, data) => {
996
- const filepath = ext(name, config.extension);
997
- const scope = context.create(data);
998
- return output(filepath, scope).then((content) => {
999
- if (scope.getExtend()) {
1000
- scope.setExtend(false);
1001
- const layout = scope.getLayout();
1002
- const data = scope.clone();
1003
- return render(layout, data)
1004
- }
1005
- return content
1006
- })
1007
- };
1008
- this.configure = function (options) {
1009
- options = options || {};
1010
- configSchema(config, options);
1011
- context.configure(config, scope);
1012
- compiler.configure(config);
1013
- cache.configure(config);
1014
- template.configure(config);
1015
- return config
1016
- };
1017
- this.render = function (name, data) {
1018
- return render(name, data)
1019
- };
1020
- this.helpers = function (methods) {
1021
- context.helpers(extend(scope, methods));
1022
- };
1023
- this.preload = function (list) {
1024
- return cache.load(list || {})
1025
- };
1026
- this.create = function (options) {
1027
- return new EJS(options)
1028
- };
1029
- this.compile = function (content, path) {
1030
- return compiler.compile(content, path)
1031
- };
1032
- this.context = function (data) {
1033
- return context.create(data)
1034
- };
1035
- this.helpers({ require, render });
1036
- return this
976
+ }
1037
977
  }
1038
978
 
1039
979
  function readFile(path, template) {
@@ -1050,10 +990,11 @@ function readFile(path, template) {
1050
990
 
1051
991
  /**
1052
992
  *
1053
- * @param {EJS} ejs
1054
- * @return {function(name, options, callback): Promise<String>}
993
+ * @param {function(config: object):object} configure
994
+ * @param {function(name: string, data?: object):Promise<string>} render
995
+ * @return {function(name:any, options:any, callback: any): Promise<void>}
1055
996
  */
1056
- function expressRenderer(ejs) {
997
+ function expressRenderer(configure, render) {
1057
998
  return function (name, options, callback) {
1058
999
  if (isFunction(options)) {
1059
1000
  callback = options;
@@ -1071,9 +1012,8 @@ function expressRenderer(ejs) {
1071
1012
  const filename = path.relative(viewPath, name);
1072
1013
  viewOptions.path = viewPath;
1073
1014
  viewOptions.cache = viewCache;
1074
- ejs.configure(viewOptions);
1075
- return ejs
1076
- .render(filename, options)
1015
+ configure(viewOptions);
1016
+ return render(filename, options)
1077
1017
  .then((content) => {
1078
1018
  callback(null, content);
1079
1019
  })
@@ -1083,10 +1023,11 @@ function expressRenderer(ejs) {
1083
1023
  }
1084
1024
  }
1085
1025
 
1086
- const ejs = new EJS({ resolver: readFile });
1087
-
1088
- const { render, context, compile, helpers, preload, configure, create } = ejs;
1026
+ const { render, context, compile, helpers, preload, configure, create } =
1027
+ new EJS({
1028
+ resolver: readFile,
1029
+ });
1089
1030
 
1090
- const __express = expressRenderer(ejs);
1031
+ const __express = expressRenderer(configure, render);
1091
1032
 
1092
1033
  export { TemplateError, TemplateNotFound, TemplateSyntaxError, __express, compile, configure, context, create, helpers, preload, render };