@funcstache/stache-stream 0.2.2 → 0.2.3

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 (109) hide show
  1. package/.eslintrc.json +30 -0
  2. package/.swcrc +29 -0
  3. package/DEV.md +84 -0
  4. package/README.md +145 -0
  5. package/TASKS.md +13 -0
  6. package/TODO.md +28 -0
  7. package/docs/.nojekyll +1 -0
  8. package/docs/assets/hierarchy.js +1 -0
  9. package/docs/assets/highlight.css +120 -0
  10. package/docs/assets/icons.js +18 -0
  11. package/docs/assets/icons.svg +1 -0
  12. package/docs/assets/main.js +60 -0
  13. package/docs/assets/navigation.js +1 -0
  14. package/docs/assets/search.js +1 -0
  15. package/docs/assets/style.css +1633 -0
  16. package/docs/classes/StacheTransformStream.html +13 -0
  17. package/docs/hierarchy.html +1 -0
  18. package/docs/index.html +73 -0
  19. package/docs/interfaces/Context.html +3 -0
  20. package/docs/interfaces/ContextProvider.html +10 -0
  21. package/docs/interfaces/PartialTagContextLambda.html +11 -0
  22. package/docs/interfaces/PartialTagContextLambdaResult.html +7 -0
  23. package/docs/interfaces/SectionTagCallback.html +12 -0
  24. package/docs/interfaces/SectionTagContextRecord.html +4 -0
  25. package/docs/interfaces/Tag.html +45 -0
  26. package/docs/interfaces/VariableTagContextLambda.html +4 -0
  27. package/docs/interfaces/VariableTagContextRecord.html +3 -0
  28. package/docs/media/StacheStream.ts +79 -0
  29. package/docs/modules.html +1 -0
  30. package/docs/types/ContextTypes.html +3 -0
  31. package/docs/types/JsonType.html +2 -0
  32. package/docs/types/PartialTagContext.html +4 -0
  33. package/docs/types/SectionTagContext.html +4 -0
  34. package/docs/types/TemplateName.html +9 -0
  35. package/docs/types/VariableTagContext.html +4 -0
  36. package/docs/types/VariableTagContextPrimitive.html +3 -0
  37. package/docs-assets/images/context-dotted-found.png +0 -0
  38. package/docs-assets/images/context-dotted-not-found.png +0 -0
  39. package/docs-assets/images/context-not-found.png +0 -0
  40. package/package.json +3 -6
  41. package/project.json +26 -0
  42. package/src/global.d.ts +10 -0
  43. package/src/index.ts +67 -0
  44. package/src/lib/parse/Parse.spec.ts +50 -0
  45. package/src/lib/parse/Parse.ts +92 -0
  46. package/src/lib/parse/README.md +62 -0
  47. package/src/lib/plan_base_v2.md +33 -0
  48. package/src/lib/plan_comment.md +53 -0
  49. package/src/lib/plan_implicit-iterator.md +213 -0
  50. package/src/lib/plan_inverted-sections.md +160 -0
  51. package/src/lib/plan_partials.md +237 -0
  52. package/src/lib/plan_sections.md +167 -0
  53. package/src/lib/plan_stache-stream.md +110 -0
  54. package/src/lib/plan_whitespace.md +98 -0
  55. package/src/lib/queue/Queue.spec.ts +275 -0
  56. package/src/lib/queue/Queue.ts +253 -0
  57. package/src/lib/queue/README.md +110 -0
  58. package/src/lib/stache-stream/README.md +45 -0
  59. package/src/lib/stache-stream/StacheStream.spec.ts +107 -0
  60. package/src/lib/stache-stream/StacheStream.ts +79 -0
  61. package/src/lib/tag/README.md +95 -0
  62. package/src/lib/tag/Tag.spec.ts +212 -0
  63. package/src/lib/tag/Tag.ts +295 -0
  64. package/src/lib/template/README.md +102 -0
  65. package/src/lib/template/Template-comment.spec.ts +76 -0
  66. package/src/lib/template/Template-inverted-section.spec.ts +85 -0
  67. package/src/lib/template/Template-partials.spec.ts +125 -0
  68. package/src/lib/template/Template-section.spec.ts +142 -0
  69. package/src/lib/template/Template.spec.ts +178 -0
  70. package/src/lib/template/Template.ts +614 -0
  71. package/src/lib/test/streams.ts +36 -0
  72. package/src/lib/tokenize/README.md +97 -0
  73. package/src/lib/tokenize/Tokenize.spec.ts +364 -0
  74. package/src/lib/tokenize/Tokenize.ts +374 -0
  75. package/src/lib/{types.d.ts → types.ts} +73 -25
  76. package/tsconfig.json +21 -0
  77. package/tsconfig.lib.json +16 -0
  78. package/tsconfig.spec.json +21 -0
  79. package/typedoc.mjs +15 -0
  80. package/vite.config.ts +27 -0
  81. package/vitest.setup.ts +6 -0
  82. package/src/global.d.js +0 -8
  83. package/src/global.d.js.map +0 -1
  84. package/src/index.d.ts +0 -7
  85. package/src/index.js +0 -24
  86. package/src/index.js.map +0 -1
  87. package/src/lib/parse/Parse.d.ts +0 -14
  88. package/src/lib/parse/Parse.js +0 -79
  89. package/src/lib/parse/Parse.js.map +0 -1
  90. package/src/lib/queue/Queue.d.ts +0 -32
  91. package/src/lib/queue/Queue.js +0 -181
  92. package/src/lib/queue/Queue.js.map +0 -1
  93. package/src/lib/stache-stream/StacheStream.d.ts +0 -22
  94. package/src/lib/stache-stream/StacheStream.js +0 -71
  95. package/src/lib/stache-stream/StacheStream.js.map +0 -1
  96. package/src/lib/tag/Tag.d.ts +0 -33
  97. package/src/lib/tag/Tag.js +0 -231
  98. package/src/lib/tag/Tag.js.map +0 -1
  99. package/src/lib/template/Template.d.ts +0 -18
  100. package/src/lib/template/Template.js +0 -428
  101. package/src/lib/template/Template.js.map +0 -1
  102. package/src/lib/test/streams.d.ts +0 -2
  103. package/src/lib/test/streams.js +0 -39
  104. package/src/lib/test/streams.js.map +0 -1
  105. package/src/lib/tokenize/Tokenize.d.ts +0 -22
  106. package/src/lib/tokenize/Tokenize.js +0 -268
  107. package/src/lib/tokenize/Tokenize.js.map +0 -1
  108. package/src/lib/types.js +0 -33
  109. package/src/lib/types.js.map +0 -1
@@ -0,0 +1,374 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { Log } from "@funcstache/logger";
3
+ import { Tag } from "../tag/Tag";
4
+
5
+ export class Tokenize extends EventEmitter<TokenizeEventMap> {
6
+ readonly #TOKEN_ENTER_CHAR = "{";
7
+ readonly #TOKEN_EXIT_CHAR = "}";
8
+
9
+ #logger = new Log({
10
+ category: "TKZ",
11
+ level: (process.env as any).LOG_LEVEL || "warn",
12
+ });
13
+
14
+ #stateCurrent: StateNames = "outsideTag";
15
+ #statePrevious: StateNames | undefined;
16
+ #leftBuffer = "";
17
+ #leftTokenBuffer = "";
18
+ #middleBuffer = "";
19
+ #rightTokenBuffer = "";
20
+
21
+ #emitTagToken() {
22
+ if (this.#leftTokenBuffer.length === this.#rightTokenBuffer.length) {
23
+ const data = Tag.parse(
24
+ `${this.#leftTokenBuffer}${this.#middleBuffer}${this.#rightTokenBuffer}`
25
+ );
26
+
27
+ data &&
28
+ this.#emitToken({
29
+ data,
30
+ uid: performance.now(),
31
+ type: "tag",
32
+ });
33
+ }
34
+
35
+ this.#leftBuffer = "";
36
+ this.#leftTokenBuffer = "";
37
+ this.#middleBuffer = "";
38
+ this.#rightTokenBuffer = "";
39
+ }
40
+
41
+ #emitToken(event: TokenizeTagEvent | TokenizeTextEvent | null) {
42
+ this.#logger.debug(() => [`!!! #emitEvent: event=`, JSON.stringify(event)]);
43
+ this.emit("token", event);
44
+ }
45
+
46
+ async push(char: string | null): Promise<void> {
47
+ this.#logger.debug(() => "-".repeat(80));
48
+
49
+ char && this.#updateStatus(char);
50
+ await this.#processChar(char);
51
+ }
52
+
53
+ async #processChar(char: string | null): Promise<void> {
54
+ this.#logger.debug(
55
+ () =>
56
+ `=== #processChar: current=${this.#stateCurrent}, previous=${
57
+ this.#statePrevious
58
+ }`
59
+ );
60
+
61
+ if (char === null) {
62
+ if (this.#leftBuffer) {
63
+ this.#emitToken({
64
+ data: this.#leftBuffer,
65
+ type: "text",
66
+ uid: performance.now(),
67
+ });
68
+ }
69
+ this.#emitTagToken();
70
+ this.#emitToken(null);
71
+ return;
72
+ }
73
+
74
+ // The only state changes that should happen here are resets that need to be done after `char`
75
+ // is processed.
76
+ switch (this.#stateCurrent) {
77
+ case "tagEnteringMaybe": {
78
+ // This state change happens in the case of nested tags, for example: `{{#a}}{{m}}{{/a}}`
79
+ // It's why we can't set `#leftTokenBuffer` in the `#updateStatus` method.
80
+ if (this.#statePrevious === "tagExitingMaybe") {
81
+ this.#emitTagToken();
82
+ }
83
+
84
+ this.#leftTokenBuffer += char;
85
+
86
+ break;
87
+ }
88
+
89
+ // case "tagExitingMaybe": {
90
+ // break;
91
+ // }
92
+
93
+ case "inTagValue": {
94
+ if (this.#leftBuffer) {
95
+ this.#emitToken({
96
+ data: this.#leftBuffer,
97
+ type: "text",
98
+ uid: performance.now(),
99
+ });
100
+ this.#leftBuffer = "";
101
+ }
102
+ break;
103
+ }
104
+
105
+ case "outsideTag": {
106
+ if (this.#statePrevious === "tagEnteringMaybe") {
107
+ // Single `{` that was not the start of a mustache tag — treat as plain text.
108
+ // #updateStatus already appended the current char to leftBuffer, so insert
109
+ // leftTokenBuffer before that last char to preserve the correct order.
110
+ this.#leftBuffer =
111
+ this.#leftBuffer.slice(0, -1) +
112
+ this.#leftTokenBuffer +
113
+ this.#leftBuffer.slice(-1);
114
+ this.#leftTokenBuffer = "";
115
+ } else if (this.#leftTokenBuffer && this.#rightTokenBuffer) {
116
+ const data = Tag.parse(
117
+ `${this.#leftTokenBuffer}${this.#middleBuffer}${
118
+ this.#rightTokenBuffer
119
+ }`
120
+ );
121
+
122
+ data &&
123
+ this.#emitToken({
124
+ data,
125
+ type: "tag",
126
+ uid: performance.now(),
127
+ });
128
+
129
+ this.#leftTokenBuffer = "";
130
+ this.#middleBuffer = "";
131
+ this.#rightTokenBuffer = "";
132
+ }
133
+ break;
134
+ }
135
+ }
136
+
137
+ this.#logger.debug(
138
+ () =>
139
+ `--- #processChar: '${char}' - ${this.#leftBuffer},${
140
+ this.#leftTokenBuffer ? ` _${this.#leftTokenBuffer}_` : " "
141
+ }, ${this.#middleBuffer}, ${
142
+ this.#rightTokenBuffer ? `_${this.#rightTokenBuffer}_` : ""
143
+ }`
144
+ );
145
+ }
146
+
147
+ #updateStateFromModel(
148
+ current: StateNames,
149
+ options: UpdateStateModelOptions,
150
+ instantStateCallback?: (state: StateNames) => void
151
+ ): StateNames {
152
+ const currentStateData = stateModel[current];
153
+ let nextState: StateNames = current;
154
+ for (const stateName of Object.keys(currentStateData) as StateNames[]) {
155
+ const dataArr = currentStateData[stateName];
156
+ if (!dataArr) {
157
+ continue;
158
+ }
159
+
160
+ if (
161
+ dataArr.some((data) => {
162
+ const allFalse =
163
+ "isFalse" in data
164
+ ? data.isFalse.every(
165
+ (optionName) => options[optionName] === false
166
+ )
167
+ : true;
168
+
169
+ if (!allFalse) {
170
+ return false;
171
+ }
172
+
173
+ const allTrue =
174
+ "isTrue" in data
175
+ ? data.isTrue.every((optionName) => options[optionName] === true)
176
+ : true;
177
+
178
+ if (allTrue) {
179
+ nextState = stateName;
180
+
181
+ // If the next state is "instant" invoke the callback then invoke
182
+ // `#updateStateFromModel` function again recursively.
183
+ if ("instant" in data && data.instant) {
184
+ instantStateCallback && instantStateCallback(nextState);
185
+ nextState = this.#updateStateFromModel(nextState, options);
186
+ }
187
+
188
+ return true;
189
+ }
190
+
191
+ return false;
192
+ })
193
+ ) {
194
+ break;
195
+ }
196
+ }
197
+
198
+ return nextState;
199
+ }
200
+
201
+ #updateStatus(char: string): void {
202
+ this.#logger.debug(
203
+ () =>
204
+ `--- #updateStatus: '${char}' - ${this.#leftBuffer},${
205
+ this.#leftTokenBuffer ? ` _${this.#leftTokenBuffer}_` : " "
206
+ }, ${this.#middleBuffer}, ${
207
+ this.#rightTokenBuffer ? `_${this.#rightTokenBuffer}_` : ""
208
+ }`
209
+ );
210
+
211
+ this.#statePrevious = this.#stateCurrent;
212
+
213
+ // This code sets the state for how `char` should be processed.
214
+ const isEnterToken = char === this.#TOKEN_ENTER_CHAR;
215
+ const isExitToken = char === this.#TOKEN_EXIT_CHAR;
216
+
217
+ const options: UpdateStateModelOptions = {
218
+ currentCharEnterToken: isEnterToken,
219
+ currentCharExitToken: isExitToken,
220
+ currentCharNotToken: !isEnterToken && !isExitToken,
221
+ enterTokenCountEqualsOne: 1 === this.#leftTokenBuffer.length,
222
+ enterTokenCountEqualsTwo: 2 === this.#leftTokenBuffer.length,
223
+ enterTokenCountEqualsThree: 3 === this.#leftTokenBuffer.length,
224
+ exitTokenCountEqualsEnterTokenCount:
225
+ this.#leftTokenBuffer.length === this.#rightTokenBuffer.length,
226
+ };
227
+
228
+ this.#stateCurrent = this.#updateStateFromModel(
229
+ this.#stateCurrent,
230
+ options,
231
+ (state) => {
232
+ this.#logger.debug(
233
+ () =>
234
+ `>>> #updateStatus: instant;\n #statePrevious='${
235
+ this.#statePrevious
236
+ }',\n state='${state}'`
237
+ );
238
+ }
239
+ );
240
+
241
+ // Manage most of the state changes here. a small number of them have to be done in
242
+ // `#processChar` because certain state values have to be reset after the char is processed.
243
+ switch (this.#stateCurrent) {
244
+ case "tagEnteringMaybe": {
245
+ //Changing to "tagEnteringMaybe" could happen because of nested tags, for example:
246
+ // `{{#a}}{{m}}{{/a}}`. It's why we can't set `#leftTokenBuffer` here, it has to be done as
247
+ // part of #processChar.
248
+
249
+ // this.#leftTokenBuffer += char;
250
+ break;
251
+ }
252
+
253
+ case "tagExitingMaybe": {
254
+ this.#rightTokenBuffer += char;
255
+ break;
256
+ }
257
+
258
+ case "outsideTag": {
259
+ this.#leftBuffer += char;
260
+ break;
261
+ }
262
+
263
+ case "inTagValue": {
264
+ this.#middleBuffer += char;
265
+ break;
266
+ }
267
+ }
268
+ }
269
+ }
270
+
271
+ interface TokenizeEvent {
272
+ uid: number;
273
+ }
274
+
275
+ export interface TokenizeTagEvent extends TokenizeEvent {
276
+ data: Tag;
277
+ type: "tag";
278
+ }
279
+
280
+ export interface TokenizeTextEvent extends TokenizeEvent {
281
+ data: string;
282
+ type: "text";
283
+ }
284
+
285
+ export type TokenizeEventMap = {
286
+ token: [TokenizeTagEvent | TokenizeTextEvent | null];
287
+ };
288
+
289
+ export type StateNames =
290
+ | "inTagValue"
291
+ | "outsideTag"
292
+ | "tagExitingMaybe"
293
+ | "tagEnteringMaybe";
294
+
295
+ const stateModel: StateModel = {
296
+ inTagValue: {
297
+ tagExitingMaybe: [
298
+ {
299
+ isTrue: ["currentCharExitToken"],
300
+ },
301
+ ],
302
+ },
303
+
304
+ outsideTag: {
305
+ tagEnteringMaybe: [
306
+ {
307
+ isTrue: ["currentCharEnterToken"],
308
+ },
309
+ ],
310
+ },
311
+
312
+ tagExitingMaybe: {
313
+ tagEnteringMaybe: [
314
+ {
315
+ isTrue: [
316
+ "currentCharEnterToken",
317
+ "exitTokenCountEqualsEnterTokenCount",
318
+ ],
319
+ },
320
+ ],
321
+ outsideTag: [
322
+ {
323
+ isTrue: ["currentCharNotToken", "exitTokenCountEqualsEnterTokenCount"],
324
+ },
325
+ ],
326
+ },
327
+
328
+ tagEnteringMaybe: {
329
+ outsideTag: [
330
+ {
331
+ isFalse: ["currentCharEnterToken"],
332
+ isTrue: ["enterTokenCountEqualsOne"],
333
+ },
334
+ ],
335
+ inTagValue: [
336
+ {
337
+ isTrue: ["currentCharNotToken", "enterTokenCountEqualsTwo"],
338
+ },
339
+ {
340
+ isTrue: ["enterTokenCountEqualsThree"],
341
+ },
342
+ ],
343
+ },
344
+ } as const;
345
+
346
+ type StateModel = {
347
+ [currentState in StateNames]: {
348
+ [moveToStateWhen in StateNames]?: (
349
+ | {
350
+ instant?: boolean;
351
+ isFalse: (keyof UpdateStateModelOptions)[];
352
+ isTrue: (keyof UpdateStateModelOptions)[];
353
+ }
354
+ | {
355
+ instant?: boolean;
356
+ isFalse: (keyof UpdateStateModelOptions)[];
357
+ }
358
+ | {
359
+ instant?: boolean;
360
+ isTrue: (keyof UpdateStateModelOptions)[];
361
+ }
362
+ )[];
363
+ };
364
+ };
365
+
366
+ interface UpdateStateModelOptions {
367
+ currentCharEnterToken: boolean;
368
+ currentCharExitToken: boolean;
369
+ currentCharNotToken: boolean;
370
+ enterTokenCountEqualsOne: boolean;
371
+ enterTokenCountEqualsTwo: boolean;
372
+ enterTokenCountEqualsThree: boolean;
373
+ exitTokenCountEqualsEnterTokenCount: boolean;
374
+ }
@@ -1,14 +1,24 @@
1
1
  import { ReadableStream } from "node:stream/web";
2
+
2
3
  export type ContentTagTagTypes = (typeof ContentTagTypes)[number];
3
- export declare const ContentTagTypes: readonly ["block", "end", "inverted", "parent", "section"];
4
+
5
+ export const ContentTagTypes = [
6
+ "block", // `{{$tag}}{{/tag}}`
7
+ "end", // `{{/tag}}`
8
+ "inverted", // `{{^tag}}{{/tag}}`
9
+ "parent", // `{{<tag}}{{/tag}}`
10
+ "section", // `{{#tag}}{{/tag}}`
11
+ ] as const;
12
+
4
13
  /**
5
14
  * A JavaScript object that supplies replacements for tags in a Mustache template. The Mustache
6
15
  * specification uses the term "hash." In stache-stream "hash" and "context" are synonyms.
7
16
  * @category Context
8
17
  */
9
18
  export interface Context {
10
- readonly [key: string]: ContextTypes | undefined;
19
+ readonly [key: string]: ContextTypes | undefined;
11
20
  }
21
+
12
22
  /**
13
23
  * When a Mustache template is rendered data needs to be provided to replace tags in the template; a
14
24
  * ContextProvider delivers that information. The ContextProvider includes context for a template as
@@ -20,19 +30,30 @@ export interface Context {
20
30
  * look for a value in parent context.
21
31
  */
22
32
  export interface ContextProvider {
23
- readonly context: ContextTypes;
24
- readonly getContextValue?: <CTX extends ContextTypes>(tag: Tag) => Promise<CTX | undefined>;
33
+ readonly context: ContextTypes;
34
+ readonly getContextValue?: <CTX extends ContextTypes>(
35
+ tag: Tag
36
+ ) => Promise<CTX | undefined>;
25
37
  }
38
+
26
39
  /**
27
40
  * Properties in a { @link Context } object implement these types. The specific types to implement
28
41
  * are determined by the tag type in a Mustache template.
29
42
  * @category Context
30
43
  */
31
- export type ContextTypes = Context | PartialTagContext | SectionTagContext | VariableTagContext;
44
+ export type ContextTypes =
45
+ | Context
46
+ // | JsonType
47
+ | PartialTagContext
48
+ | SectionTagContext
49
+ | VariableTagContext;
50
+ // | (Context | JsonType | SectionTagContextList)[];
51
+
32
52
  /**
33
53
  * A primitive JavaScript type that can be serialized in a JSON object.
34
54
  */
35
55
  export type JsonType = boolean | number | string;
56
+
36
57
  /**
37
58
  * Implement one of these interfaces to provide replacements for
38
59
  * {@link https://mustache.github.io/mustache.5.html#Variables Partial tags}. See
@@ -41,6 +62,7 @@ export type JsonType = boolean | number | string;
41
62
  * @category Partial
42
63
  */
43
64
  export type PartialTagContext = PartialTagContextLambda;
65
+
44
66
  /**
45
67
  * Implement this function to asynchronously return a `ReadableStream` that contains a Mustache
46
68
  * template and its associated context that replaces a
@@ -62,20 +84,22 @@ export type PartialTagContext = PartialTagContextLambda;
62
84
  * @category Partial
63
85
  */
64
86
  export interface PartialTagContextLambda {
65
- (tag: Tag): Promise<PartialTagContextLambdaResult>;
87
+ (tag: Tag): Promise<PartialTagContextLambdaResult>;
66
88
  }
89
+
67
90
  /**
68
91
  * The result of invoking a {@link PartialTagContextLambda}.
69
92
  * @category Context
70
93
  * @category Partial
71
94
  */
72
95
  export interface PartialTagContextLambdaResult {
73
- /** The optional context associated with this template. */
74
- context?: ContextTypes;
75
- /** When invoked `input` resolves to a stream containing the Mustache template that replaces a
76
- * partial tag. */
77
- input: () => Promise<ReadableStream>;
96
+ /** The optional context associated with this template. */
97
+ context?: ContextTypes;
98
+ /** When invoked `input` resolves to a stream containing the Mustache template that replaces a
99
+ * partial tag. */
100
+ input: () => Promise<ReadableStream>;
78
101
  }
102
+
79
103
  /**
80
104
  * Implement one of these interfaces to provide replacements for
81
105
  * {@link https://mustache.github.io/mustache.5.html#Sections Section tags}. See
@@ -84,6 +108,7 @@ export interface PartialTagContextLambdaResult {
84
108
  * @category Section
85
109
  */
86
110
  export type SectionTagContext = SectionTagCallback | SectionTagContextRecord[];
111
+
87
112
  /**
88
113
  * Provides zero or more items that will replace a
89
114
  * {@link https://mustache.github.io/mustache.5.html#Sections Section tag}. See
@@ -92,8 +117,9 @@ export type SectionTagContext = SectionTagCallback | SectionTagContextRecord[];
92
117
  * @category Section
93
118
  */
94
119
  export interface SectionTagContextRecord {
95
- [key: string]: JsonType | SectionTagContextRecord;
120
+ [key: string]: JsonType | SectionTagContextRecord;
96
121
  }
122
+
97
123
  /**
98
124
  * When provided this callback is invoked when the
99
125
  * {@link https://mustache.github.io/mustache.5.html#Sections Section tag} is encountered. The
@@ -117,8 +143,9 @@ export interface SectionTagContextRecord {
117
143
  * @category Section
118
144
  */
119
145
  export interface SectionTagCallback {
120
- (tagContent: string): string;
146
+ (tagContent: string): string;
121
147
  }
148
+
122
149
  /**
123
150
  * Represents a tag in a Mustache template providing information about a tag and its
124
151
  * characteristics.
@@ -165,12 +192,14 @@ export interface SectionTagCallback {
165
192
  * "{{ #items }}" the `value` is "#items".
166
193
  */
167
194
  export interface Tag {
168
- readonly content: string | null;
169
- readonly key: string;
170
- readonly raw: boolean;
171
- readonly type: ContentTagTagTypes | ValueTagTagTypes;
172
- readonly value: string;
195
+ readonly content: string | null;
196
+ readonly key: string;
197
+ readonly raw: boolean;
198
+ readonly type: ContentTagTagTypes | ValueTagTagTypes;
199
+ readonly value: string;
200
+ // process: () => Promise<void>;
173
201
  }
202
+
174
203
  /**
175
204
  * Defines how to access a template file.
176
205
  * - string: The name of a template file in the same directory as this module file. The name must
@@ -179,9 +208,19 @@ export interface Tag {
179
208
  * in the "string" type above. The second element is a relative path to the directory where the
180
209
  * template file is located; the path is relative to the location of this module file.
181
210
  */
182
- export type TemplateName = string | [templateName: string, templateFilename: string];
211
+ export type TemplateName =
212
+ | string
213
+ | [templateName: string, templateFilename: string];
214
+
183
215
  export type ValueTagTagTypes = (typeof ValueTagTypes)[number];
184
- export declare const ValueTagTypes: readonly ["comment", "implicit", "partial", "variable"];
216
+
217
+ export const ValueTagTypes = [
218
+ "comment", // {{!text}}
219
+ "implicit", // `{{.}}`
220
+ "partial", // `{{>tag}}`
221
+ "variable",
222
+ ] as const;
223
+
185
224
  /**
186
225
  * Interfaces that can supply replacements for
187
226
  * {@link https://mustache.github.io/mustache.5.html#Variables variable tags}. See
@@ -189,7 +228,11 @@ export declare const ValueTagTypes: readonly ["comment", "implicit", "partial",
189
228
  * @category Context
190
229
  * @category Variable
191
230
  */
192
- export type VariableTagContext = VariableTagContextPrimitive | VariableTagContextLambda | VariableTagContextRecord;
231
+ export type VariableTagContext =
232
+ | VariableTagContextPrimitive
233
+ | VariableTagContextLambda
234
+ | VariableTagContextRecord;
235
+
193
236
  /**
194
237
  * A function that resolves to the value that replaces a
195
238
  * {@link https://mustache.github.io/mustache.5.html#Variables variable tag}. See
@@ -198,15 +241,19 @@ export type VariableTagContext = VariableTagContextPrimitive | VariableTagContex
198
241
  * @category Variable
199
242
  */
200
243
  export interface VariableTagContextLambda {
201
- (): Promise<VariableTagContextPrimitive | VariableTagContextRecord>;
244
+ (): Promise<VariableTagContextPrimitive | VariableTagContextRecord>;
202
245
  }
246
+
203
247
  /**
204
248
  * A {@link https://mustache.github.io/mustache.5.html#Variables variable tag} can be replaced by
205
249
  * these JavaScript primitives. See {@link VariableTagContext VariableTagContext}.
206
250
  * @category Context
207
251
  * @category Variable
208
252
  */
209
- export type VariableTagContextPrimitive = JsonType | Array<boolean | number | string>;
253
+ export type VariableTagContextPrimitive =
254
+ | JsonType
255
+ | Array<boolean | number | string>;
256
+
210
257
  /**
211
258
  * A {@link https://mustache.github.io/mustache.5.html#Variables variable tag} with a "dotted name"
212
259
  * can be replaced by a JavaScript object. See {@link VariableTagContext VariableTagContext}.
@@ -214,11 +261,12 @@ export type VariableTagContextPrimitive = JsonType | Array<boolean | number | st
214
261
  * @category Variable
215
262
  */
216
263
  export interface VariableTagContextRecord {
217
- [key: string]: unknown;
264
+ [key: string]: unknown;
218
265
  }
266
+
219
267
  /**
220
268
  * Invoke this function to write to the output `WritableStream`.
221
269
  */
222
270
  export interface WriteToOutput {
223
- (text: string): Promise<void>;
271
+ (text: string): Promise<void>;
224
272
  }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "forceConsistentCasingInFileNames": true,
5
+ "strict": true,
6
+ "noImplicitOverride": true,
7
+ "noPropertyAccessFromIndexSignature": true,
8
+ "noImplicitReturns": true,
9
+ "noFallthroughCasesInSwitch": true
10
+ },
11
+ "files": [],
12
+ "include": [],
13
+ "references": [
14
+ {
15
+ "path": "./tsconfig.lib.json"
16
+ },
17
+ {
18
+ "path": "./tsconfig.spec.json"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "declaration": true,
6
+ "types": ["node"]
7
+ },
8
+ "include": ["src/**/*.ts"],
9
+ "exclude": [
10
+ "vite.config.ts",
11
+ "vitest.config.ts",
12
+ "vitest.setup.ts",
13
+ "src/**/*.spec.ts",
14
+ "src/**/*.test.ts"
15
+ ]
16
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "types": [
6
+ "vitest/globals",
7
+ "vitest/importMeta",
8
+ "vite/client",
9
+ "node",
10
+ "vitest"
11
+ ]
12
+ },
13
+ "include": [
14
+ "vite.config.ts",
15
+ "vitest.config.ts",
16
+ "vitest.setup.ts",
17
+ "src/**/*.d.ts",
18
+ "src/**/*.test.ts",
19
+ "src/**/*.spec.ts"
20
+ ]
21
+ }
package/typedoc.mjs ADDED
@@ -0,0 +1,15 @@
1
+ // what?
2
+ const config = {
3
+ entryPoints: ["src/index.ts"],
4
+ includeVersion: true,
5
+ githubPages: true,
6
+ gitRevision: "main",
7
+ // navigation: {
8
+ // includeCategories: true,
9
+ // },
10
+ out: "docs",
11
+ readme: ["README.md"],
12
+ tsconfig: "tsconfig.lib.json",
13
+ };
14
+
15
+ export default config;
package/vite.config.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from "vite";
2
+ import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin";
3
+
4
+ export default defineConfig({
5
+ root: __dirname,
6
+ cacheDir: "../../node_modules/.vite/libs/stache-stream",
7
+
8
+ plugins: [nxViteTsPaths()],
9
+
10
+ // Uncomment this if you are using workers.
11
+ // worker: {
12
+ // plugins: [ nxViteTsPaths() ],
13
+ // },
14
+
15
+ test: {
16
+ watch: false,
17
+ globals: true,
18
+ environment: "node",
19
+ include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
20
+ reporters: ["default"],
21
+ setupFiles: ["./vitest.setup.ts"],
22
+ coverage: {
23
+ reportsDirectory: "../../coverage/libs/stache-stream",
24
+ provider: "v8",
25
+ },
26
+ },
27
+ });
@@ -0,0 +1,6 @@
1
+ import { populateGlobal } from "vitest/environments";
2
+ import "./src/index";
3
+
4
+ beforeAll(() => {
5
+ populateGlobal({}, globalThis);
6
+ });