@reidelsaltres/pureper 0.1.157 → 0.1.162

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.
Files changed (107) hide show
  1. package/out/foundation/Triplet.d.ts.map +1 -1
  2. package/out/foundation/Triplet.js +4 -5
  3. package/out/foundation/Triplet.js.map +1 -1
  4. package/out/foundation/api/Observer.d.ts +30 -0
  5. package/out/foundation/api/Observer.d.ts.map +1 -1
  6. package/out/foundation/api/Observer.js +48 -0
  7. package/out/foundation/api/Observer.js.map +1 -1
  8. package/out/foundation/component_api/Component.d.ts +2 -2
  9. package/out/foundation/component_api/Component.d.ts.map +1 -1
  10. package/out/foundation/component_api/Component.js.map +1 -1
  11. package/out/foundation/component_api/UniHtml.d.ts +4 -10
  12. package/out/foundation/component_api/UniHtml.d.ts.map +1 -1
  13. package/out/foundation/component_api/UniHtml.js +7 -15
  14. package/out/foundation/component_api/UniHtml.js.map +1 -1
  15. package/out/foundation/engine/BalancedParser.d.ts +58 -0
  16. package/out/foundation/engine/BalancedParser.d.ts.map +1 -0
  17. package/out/foundation/engine/BalancedParser.js +301 -0
  18. package/out/foundation/engine/BalancedParser.js.map +1 -0
  19. package/out/foundation/engine/EscapeHandler.d.ts +27 -0
  20. package/out/foundation/engine/EscapeHandler.d.ts.map +1 -0
  21. package/out/foundation/engine/EscapeHandler.js +47 -0
  22. package/out/foundation/engine/EscapeHandler.js.map +1 -0
  23. package/out/foundation/engine/Expression.d.ts +83 -0
  24. package/out/foundation/engine/Expression.d.ts.map +1 -0
  25. package/out/foundation/engine/Expression.js +256 -0
  26. package/out/foundation/engine/Expression.js.map +1 -0
  27. package/out/foundation/engine/Rule.d.ts +85 -0
  28. package/out/foundation/engine/Rule.d.ts.map +1 -0
  29. package/out/foundation/engine/Rule.js +69 -0
  30. package/out/foundation/engine/Rule.js.map +1 -0
  31. package/out/foundation/engine/Scope.d.ts +61 -0
  32. package/out/foundation/engine/Scope.d.ts.map +1 -0
  33. package/out/foundation/engine/Scope.js +156 -0
  34. package/out/foundation/engine/Scope.js.map +1 -0
  35. package/out/foundation/engine/TemplateEngine.d.ts +96 -0
  36. package/out/foundation/engine/TemplateEngine.d.ts.map +1 -0
  37. package/out/foundation/engine/TemplateEngine.js +235 -0
  38. package/out/foundation/engine/TemplateEngine.js.map +1 -0
  39. package/out/foundation/engine/TemplateInstance.d.ts +241 -0
  40. package/out/foundation/engine/TemplateInstance.d.ts.map +1 -0
  41. package/out/foundation/engine/TemplateInstance.js +637 -0
  42. package/out/foundation/engine/TemplateInstance.js.map +1 -0
  43. package/out/foundation/engine/TemplateInstance.old.d.ts +219 -0
  44. package/out/foundation/engine/TemplateInstance.old.d.ts.map +1 -0
  45. package/out/foundation/engine/TemplateInstance.old.js +487 -0
  46. package/out/foundation/engine/TemplateInstance.old.js.map +1 -0
  47. package/out/foundation/engine/exceptions/TemplateExceptions.d.ts +21 -0
  48. package/out/foundation/engine/exceptions/TemplateExceptions.d.ts.map +1 -0
  49. package/out/foundation/engine/exceptions/TemplateExceptions.js +26 -0
  50. package/out/foundation/engine/exceptions/TemplateExceptions.js.map +1 -0
  51. package/out/foundation/engine/index.d.ts +18 -0
  52. package/out/foundation/engine/index.d.ts.map +1 -0
  53. package/out/foundation/engine/index.js +19 -0
  54. package/out/foundation/engine/index.js.map +1 -0
  55. package/out/foundation/engine/rules/attribute/EventRule.d.ts +22 -0
  56. package/out/foundation/engine/rules/attribute/EventRule.d.ts.map +1 -0
  57. package/out/foundation/engine/rules/attribute/EventRule.js +129 -0
  58. package/out/foundation/engine/rules/attribute/EventRule.js.map +1 -0
  59. package/out/foundation/engine/rules/attribute/InjectionRule.d.ts +20 -0
  60. package/out/foundation/engine/rules/attribute/InjectionRule.d.ts.map +1 -0
  61. package/out/foundation/engine/rules/attribute/InjectionRule.js +108 -0
  62. package/out/foundation/engine/rules/attribute/InjectionRule.js.map +1 -0
  63. package/out/foundation/engine/rules/attribute/RefRule.d.ts +23 -0
  64. package/out/foundation/engine/rules/attribute/RefRule.d.ts.map +1 -0
  65. package/out/foundation/engine/rules/attribute/RefRule.js +104 -0
  66. package/out/foundation/engine/rules/attribute/RefRule.js.map +1 -0
  67. package/out/foundation/engine/rules/syntax/ExpressionRule.d.ts +19 -0
  68. package/out/foundation/engine/rules/syntax/ExpressionRule.d.ts.map +1 -0
  69. package/out/foundation/engine/rules/syntax/ExpressionRule.js +82 -0
  70. package/out/foundation/engine/rules/syntax/ExpressionRule.js.map +1 -0
  71. package/out/foundation/engine/rules/syntax/ForRule.d.ts +19 -0
  72. package/out/foundation/engine/rules/syntax/ForRule.d.ts.map +1 -0
  73. package/out/foundation/engine/rules/syntax/ForRule.js +226 -0
  74. package/out/foundation/engine/rules/syntax/ForRule.js.map +1 -0
  75. package/out/foundation/engine/rules/syntax/IfRule.d.ts +17 -0
  76. package/out/foundation/engine/rules/syntax/IfRule.d.ts.map +1 -0
  77. package/out/foundation/engine/rules/syntax/IfRule.js +220 -0
  78. package/out/foundation/engine/rules/syntax/IfRule.js.map +1 -0
  79. package/out/foundation/worker/Router.d.ts.map +1 -1
  80. package/out/foundation/worker/Router.js.map +1 -1
  81. package/out/index.d.ts +2 -0
  82. package/out/index.d.ts.map +1 -1
  83. package/out/index.js +2 -0
  84. package/out/index.js.map +1 -1
  85. package/package.json +1 -1
  86. package/src/foundation/Triplet.ts +6 -6
  87. package/src/foundation/api/Observer.ts +60 -0
  88. package/src/foundation/component_api/Component.ts +2 -1
  89. package/src/foundation/component_api/UniHtml.ts +12 -22
  90. package/src/foundation/engine/BalancedParser.ts +353 -0
  91. package/src/foundation/engine/EscapeHandler.ts +54 -0
  92. package/src/foundation/engine/Expression.ts +285 -0
  93. package/src/foundation/engine/Rule.ts +138 -0
  94. package/src/foundation/engine/Scope.ts +176 -0
  95. package/src/foundation/engine/TemplateEngine.ts +318 -0
  96. package/src/foundation/engine/TemplateInstance.md +110 -0
  97. package/src/foundation/engine/TemplateInstance.old.ts +673 -0
  98. package/src/foundation/engine/TemplateInstance.ts +843 -0
  99. package/src/foundation/engine/exceptions/TemplateExceptions.ts +27 -0
  100. package/src/foundation/engine/rules/attribute/EventRule.ts +171 -0
  101. package/src/foundation/engine/rules/attribute/InjectionRule.ts +140 -0
  102. package/src/foundation/engine/rules/attribute/RefRule.ts +126 -0
  103. package/src/foundation/engine/rules/syntax/ExpressionRule.ts +102 -0
  104. package/src/foundation/engine/rules/syntax/ForRule.ts +267 -0
  105. package/src/foundation/engine/rules/syntax/IfRule.ts +261 -0
  106. package/src/foundation/worker/Router.ts +1 -1
  107. package/src/index.ts +8 -0
@@ -0,0 +1,318 @@
1
+ import Scope from './Scope.js';
2
+ import Expression from './Expression.js';
3
+ import EscapeHandler from './EscapeHandler.js';
4
+ import TemplateInstance, { TemplateSection, FragmentBinding } from './TemplateInstance.js';
5
+ import Rule, { RuleMatch, RuleResult, SyntaxRule, AttributeRule } from './Rule.js';
6
+ import Observable from '../api/Observer.js';
7
+
8
+ // Import rules
9
+ import ExpressionRule from './rules/syntax/ExpressionRule.js';
10
+ import IfRule from './rules/syntax/IfRule.js';
11
+ import ForRule from './rules/syntax/ForRule.js';
12
+ import RefRule from './rules/attribute/RefRule.js';
13
+ import EventRule from './rules/attribute/EventRule.js';
14
+ import InjectionRule from './rules/attribute/InjectionRule.js';
15
+
16
+ /**
17
+ * TemplateEngineOptions - настройки TemplateEngine
18
+ */
19
+ export interface TemplateEngineOptions {
20
+ /** Оставлять синтаксис атрибутивных Rule в финальном HTML */
21
+ showAttributeRule?: boolean;
22
+ /** Включить предупреждения отладки */
23
+ debugWarnings?: boolean;
24
+ }
25
+
26
+ /**
27
+ * ProcessResult - результат обработки шаблона
28
+ */
29
+ export interface ProcessResult {
30
+ output: string;
31
+ observables: Observable<any>[];
32
+ sections: TemplateSection[];
33
+ /** ID созданного фрагмента (если был создан) */
34
+ fragmentId?: string;
35
+ }
36
+
37
+ /**
38
+ * TemplateEngine - главный класс шаблонизатора.
39
+ * Обрабатывает HTML-шаблон с Rule и создаёт TemplateInstance.
40
+ */
41
+ export default class TemplateEngine {
42
+ private readonly scope: Scope;
43
+ private readonly options: TemplateEngineOptions;
44
+ private readonly syntaxRules: SyntaxRule[] = [];
45
+ private readonly attributeRules: AttributeRule[] = [];
46
+
47
+ constructor(scope: Scope | object, options?: TemplateEngineOptions) {
48
+ this.scope = scope instanceof Scope ? scope : Scope.from(scope);
49
+ this.options = {
50
+ showAttributeRule: false,
51
+ debugWarnings: true,
52
+ ...options
53
+ };
54
+
55
+ // Register default rules (sorted by priority)
56
+ this.registerDefaultRules();
57
+ }
58
+
59
+ /**
60
+ * Зарегистрировать стандартные правила
61
+ */
62
+ private registerDefaultRules(): void {
63
+ // Syntax rules
64
+ this.syntaxRules.push(new ForRule());
65
+ this.syntaxRules.push(new IfRule());
66
+ this.syntaxRules.push(new ExpressionRule());
67
+
68
+ // Attribute rules
69
+ this.attributeRules.push(new RefRule());
70
+ this.attributeRules.push(new EventRule());
71
+ this.attributeRules.push(new InjectionRule());
72
+
73
+ // Sort by priority
74
+ this.syntaxRules.sort((a, b) => a.priority - b.priority);
75
+ this.attributeRules.sort((a, b) => a.priority - b.priority);
76
+ }
77
+
78
+ /**
79
+ * Добавить пользовательское правило
80
+ */
81
+ public addRule(rule: Rule): void {
82
+ if (rule.type === 'syntax') {
83
+ this.syntaxRules.push(rule as SyntaxRule);
84
+ this.syntaxRules.sort((a, b) => a.priority - b.priority);
85
+ } else {
86
+ this.attributeRules.push(rule as AttributeRule);
87
+ this.attributeRules.sort((a, b) => a.priority - b.priority);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Получить Scope
93
+ */
94
+ public getScope(): Scope {
95
+ return this.scope;
96
+ }
97
+
98
+ /**
99
+ * Обработать шаблон и вернуть TemplateInstance
100
+ */
101
+ public parse(template: string): TemplateInstance {
102
+ const templateInstance = new TemplateInstance(this.scope);
103
+ const result = this.processTemplateWithFragments(template, this.scope, templateInstance, null);
104
+
105
+ // Устанавливаем корневой фрагмент
106
+ if (result.fragmentId) {
107
+ templateInstance.setRootFragment(result.fragmentId);
108
+ }
109
+
110
+ // Add sections and track observables
111
+ for (const section of result.sections) {
112
+ templateInstance.addSection(section);
113
+
114
+ // Track observables
115
+ for (const observable of section.result.observables || []) {
116
+ templateInstance.trackObservable(observable, section, (s) => {
117
+ return this.processTemplate(s.sourceTemplate, this.scope);
118
+ });
119
+ }
120
+ }
121
+
122
+ return templateInstance;
123
+ }
124
+
125
+ /**
126
+ * Обработать шаблон с созданием фрагментов
127
+ */
128
+ private processTemplateWithFragments(
129
+ template: string,
130
+ scope: Scope,
131
+ instance: TemplateInstance,
132
+ parentFragmentId: string | null
133
+ ): ProcessResult {
134
+ const result = this.processTemplate(template, scope);
135
+
136
+ // Создаём фрагмент для этого результата
137
+ const fragmentId = instance.createFragment(
138
+ result.output,
139
+ template,
140
+ result.sections,
141
+ parentFragmentId
142
+ );
143
+
144
+ return {
145
+ ...result,
146
+ fragmentId
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Обработать шаблон (внутренний метод, используется Rule для рекурсии)
152
+ */
153
+ public processTemplate(template: string, scope: Scope): ProcessResult {
154
+ const allObservables: Observable<any>[] = [];
155
+ const allSections: TemplateSection[] = [];
156
+
157
+ // Step 1: Handle @@ escape sequences
158
+ let processed = EscapeHandler.process(template, (escaped) => {
159
+ let result = escaped;
160
+
161
+ // Step 2: Process syntax rules (in priority order)
162
+ for (const rule of this.syntaxRules) {
163
+ result = this.processRule(rule, result, scope, allObservables, allSections);
164
+ }
165
+
166
+ // Step 3: Process attribute rules
167
+ for (const rule of this.attributeRules) {
168
+ // Skip injection - processed last
169
+ if (rule.name === 'injection') continue;
170
+ result = this.processRule(rule, result, scope, allObservables, allSections);
171
+ }
172
+
173
+ return result;
174
+ });
175
+
176
+ // Step 4: Process injection rules last
177
+ const injectionRule = this.attributeRules.find(r => r.name === 'injection');
178
+ if (injectionRule) {
179
+ processed = this.processRule(injectionRule, processed, scope, allObservables, allSections);
180
+ }
181
+
182
+ // Step 5: Handle showAttributeRule option
183
+ if (!this.options.showAttributeRule) {
184
+ processed = this.removeAttributeSyntax(processed);
185
+ }
186
+
187
+ return {
188
+ output: processed,
189
+ observables: allObservables,
190
+ sections: allSections
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Обработать одно правило
196
+ */
197
+ private processRule(
198
+ rule: Rule,
199
+ template: string,
200
+ scope: Scope,
201
+ observables: Observable<any>[],
202
+ sections: TemplateSection[]
203
+ ): string {
204
+ const matches = rule.find(template);
205
+
206
+ // Sort matches by position (reverse to process from end)
207
+ matches.sort((a, b) => b.start - a.start);
208
+
209
+ let result = template;
210
+
211
+ for (const match of matches) {
212
+ try {
213
+ const ruleResult = rule.execute(match, scope, this) as RuleResult;
214
+
215
+ // Track observables
216
+ if (ruleResult.observables) {
217
+ observables.push(...ruleResult.observables);
218
+ }
219
+
220
+ // Create section
221
+ const section: TemplateSection = {
222
+ rule,
223
+ match,
224
+ result: ruleResult,
225
+ sourceTemplate: match.fullMatch,
226
+ children: [],
227
+ subscriptions: []
228
+ };
229
+ sections.push(section);
230
+
231
+ // Replace in template
232
+ result = result.slice(0, match.start) + ruleResult.output + result.slice(match.end);
233
+
234
+ } catch (error) {
235
+ console.error(`[TemplateEngine] Error processing rule "${rule.name}":`, error);
236
+ // Continue processing other rules
237
+ }
238
+ }
239
+
240
+ return result;
241
+ }
242
+
243
+ /**
244
+ * Удалить синтаксис атрибутивных Rule из финального HTML
245
+ */
246
+ private removeAttributeSyntax(template: string): string {
247
+ // Remove @[ref], @on[...], @injection[...] patterns that weren't processed
248
+ return template
249
+ .replace(/@\[ref\]\s*=\s*["'][^"']*["']/gi, '')
250
+ .replace(/@on\[[a-zA-Z]+\]\s*=\s*["'][^"']*["']/gi, '')
251
+ .replace(/@injection\[(head|tail)\]\s*=\s*["'][^"']*["']/gi, '');
252
+ }
253
+
254
+ /**
255
+ * Добавить новый шаблон в существующий TemplateInstance.
256
+ * Обрабатывает шаблон и добавляет результат как новый фрагмент.
257
+ * Если instance привязан к контейнерам, DOM обновится автоматически.
258
+ *
259
+ * @param instance - существующий TemplateInstance
260
+ * @param template - новый шаблон для добавления
261
+ * @param customScope - опциональный scope для нового шаблона
262
+ * @returns ID созданного фрагмента
263
+ */
264
+ public appendTemplate(instance: TemplateInstance, template: string, customScope?: Scope | object): string {
265
+ const scope = customScope
266
+ ? (customScope instanceof Scope ? customScope : Scope.from(customScope))
267
+ : instance.getScope();
268
+
269
+ // Обрабатываем шаблон
270
+ const result = this.processTemplate(template, scope);
271
+
272
+ // Создаём фрагмент
273
+ const fragmentId = instance.createFragment(
274
+ result.output,
275
+ template,
276
+ result.sections,
277
+ null // без родителя - это отдельный фрагмент
278
+ );
279
+
280
+ // Добавляем секции и отслеживаем Observable
281
+ for (const section of result.sections) {
282
+ instance.addSection(section);
283
+
284
+ for (const observable of section.result.observables || []) {
285
+ instance.trackObservable(observable, section, (s) => {
286
+ return this.processTemplate(s.sourceTemplate, scope);
287
+ });
288
+ }
289
+ }
290
+
291
+ // Вставляем в привязанные контейнеры
292
+ instance.insertAppendedFragment(fragmentId);
293
+
294
+ return fragmentId;
295
+ }
296
+
297
+ /**
298
+ * Статический метод для быстрой обработки
299
+ */
300
+ public static process(template: string, scope: object, options?: TemplateEngineOptions): string {
301
+ const engine = new TemplateEngine(scope, options);
302
+ const TemplateInstance = engine.parse(template);
303
+ return TemplateInstance.getTemplate();
304
+ }
305
+
306
+ /**
307
+ * Создать TemplateInstance из шаблона
308
+ */
309
+ public static create(template: string, scope: object, options?: TemplateEngineOptions): TemplateInstance {
310
+ const engine = new TemplateEngine(scope, options);
311
+ return engine.parse(template);
312
+ }
313
+ }
314
+
315
+ // Re-export useful types
316
+ export { Scope, Expression, TemplateInstance, Rule, SyntaxRule, AttributeRule };
317
+ export { ExpressionRule, IfRule, ForRule, RefRule, EventRule, InjectionRule };
318
+ export * from './exceptions/TemplateExceptions.js';
@@ -0,0 +1,110 @@
1
+ Проанализируй текущий код TemplateInstance, Переделай TemplateInstance
2
+
3
+ # Предположительный пример структуры и работы
4
+
5
+ Должен содержать множество различных мелких фрагментов, и при обновлении rebuild-ить только те на которые изменения повлияли, т.е
6
+ Если:
7
+ <div>@(observable1)</div>
8
+ <div>@(observable2)</div>
9
+
10
+ должно создатся 2 фрагмента
11
+ 1. <div>@(observable1)</div> - пересоздастся только при изменении observable1, изменения observable2 не повлияют на него
12
+ 2. <div>@(observable2)</div> - пересоздастся только при изменении observable2, изменения observable1 не повлияют на него
13
+
14
+ ---
15
+
16
+ Если:
17
+ <div>@(observable1) и @(observable2)</div> то изменения observable1 и изменение observable2 должны влиять
18
+
19
+ здесь 1 фрагмент:
20
+ 1. <div>@(observable1) и @(observable2)</div> #id1
21
+
22
+ ---
23
+
24
+ Если:
25
+ <div @[ref]="'outer'">
26
+ <span>@(observable1)</span>
27
+ <span>@(observable2)</span>
28
+ @for (observableVar in observableCollection) {
29
+ <span>@(observableVar)</span>
30
+ }
31
+ </div>
32
+
33
+ здесь 5+ фрагментов:
34
+ 1. <div @[ref]="'outer'"> #id1
35
+ #id2
36
+ #id3
37
+ #id4
38
+ </div>
39
+ 2. <span>@(observable1)</span> #id2
40
+ 3. <span>@(observable2)</span> #id3
41
+ 4. *RuleFragment* @for (observableVar in observableCollection) { #id5 } #id4
42
+ 5+ <span>@(observableVar)</span> #id5
43
+
44
+ 3 отдельных фрагмента (#id5-a, #id5-b, #id5-c), каждый обновляется независимо
45
+
46
+ Если изменится observable1 то пересоздастся должен только #id2
47
+ Если изменится observableCollection то пересоздастся должен #id4 и соответсвенно это коснётся и вложенного в него #id5
48
+ Измеения в var в #id5 должно пересоздать только #id5
49
+
50
+
51
+ Если Scope { observable1 = new Observable(1),
52
+ observable1 = new Observable(2),
53
+ observableCollection = new Observable([55,56,57]) }
54
+
55
+ то результат будет выглядеть так:
56
+
57
+ <div id="'outer'">
58
+ <span>1</span>
59
+ <span>2</span>
60
+ <span>55</span>
61
+ <span>56</span>
62
+ <span>57</span>
63
+ </div>
64
+
65
+ но при изменении observable1.setObject(999) то результат станет:
66
+
67
+ <div id="'outer'">
68
+ <span>999</span>
69
+ <span>2</span>
70
+ <span>55</span>
71
+ <span>56</span>
72
+ <span>57</span>
73
+ </div>
74
+
75
+ если TemplateInstance имеет биндинг с контейнером, то аналогичные изменения должны происходить и в контейнере
76
+
77
+ ---
78
+
79
+
80
+
81
+ изменения observable1 повлияет только на #id1
82
+ изменения observabıe2 повлияет только на #id2
83
+
84
+ TemplateInstance должен иметь эмитить ивенты на которые можно подписатся
85
+ onFragmentChange(event: FragmentChangeEvent)
86
+
87
+ должен иметь методы
88
+ bindRefs() - должен создавать в Scope переменные из @[ref] правила
89
+ unbindRefs() - должен удалять из Scope переменные из @[ref] правила (приравнивать к null)
90
+
91
+ bindEvents() - должен добавлять обработчик событий к html-element из @on[event] правила
92
+ unbindEvents() - должен удалять обработчик событий из html-element по @on[event] правилу
93
+
94
+ bind(container) - должен связывать внутреннюю DOM структуру с container, должен вставлять DOM в container
95
+ unbind(container) - должен удалять связь внутренней DOM структуры с container, оставляет элементы в container, но при изменении в TemplateInstance container уже не должен меняется
96
+
97
+ Если существует binding с container то изменения внутри TemplateInstance должны так же отражатся и на container,
98
+ удаление и добавление ивентов, динамическое обновление и пересоздание фагментов,
99
+
100
+ TemplateInstance может иметь множество контейнеров binding-ов
101
+
102
+ bind(container) - должен автоматически вызывать bindRefs,bindEvents
103
+ unbind(container) - должен автоматически вызывать unbindRefs,unbindEvents
104
+
105
+ # Уточнения
106
+ 1. Архитектурная проблема — TemplateEngine не создаёт фрагменты:
107
+ Сейчас TemplateEngine.parse() возвращает один общий результат. Разбиение на фрагменты по Observable не происходит. Нужно менять логику парсинга. Изменения TemplateEngine приемлимы если требуются
108
+
109
+
110
+