spooder 4.3.0 → 4.3.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.
Files changed (3) hide show
  1. package/README.md +14 -46
  2. package/package.json +1 -1
  3. package/src/api.ts +12 -62
package/README.md CHANGED
@@ -1066,24 +1066,6 @@ const html = parse_template(template, replacements);
1066
1066
  </html>
1067
1067
  ```
1068
1068
 
1069
- Object properties are supported by using the dot syntax.
1070
-
1071
- ```ts
1072
- const template = `<span>{$foo.bar}</span>`;
1073
-
1074
- const replacements = {
1075
- foo: {
1076
- bar: 'Hello, world!';
1077
- }
1078
- };
1079
-
1080
- const html = parse_template(template, replacements);
1081
- ```
1082
-
1083
- ```html
1084
- <span>Hello, world!</span>
1085
- ```
1086
-
1087
1069
  By default, placeholders that do not appear in the replacement object will be left as-is. Set `drop_missing` to `true` to remove them.
1088
1070
 
1089
1071
  ```ts
@@ -1125,15 +1107,16 @@ parse_template('Hello {$world}', replacer);
1125
1107
  </body>
1126
1108
  </html>
1127
1109
  ```
1110
+
1128
1111
  `parse_template` supports looping arrays with the following syntax.
1129
1112
 
1130
1113
  ```html
1131
- {$for:foo as bar}My colour is {$bar}{/for}
1114
+ {$for:foo}My colour is %s{/for}
1132
1115
  ```
1133
1116
  ```ts
1134
1117
  const template = `
1135
1118
  <ul>
1136
- {$for:foo as bar}<li>{$bar}</li>{/for}
1119
+ {$for:foo}<li>%s</li>{/for}
1137
1120
  </ul>
1138
1121
  `;
1139
1122
 
@@ -1152,39 +1135,24 @@ const html = parse_template(template, replacements);
1152
1135
  </ul>
1153
1136
  ```
1154
1137
 
1155
- Loops also support object properties using the dot syntax.
1138
+ All placeholders inside a `{$for:}` loop are substituted, but only if the loop variable exists.
1156
1139
 
1157
- ```html
1158
- {$for:foo as bar}My colour is {$bar.baz}{/for}
1159
- ```
1160
- ```ts
1161
- const template = `
1162
- <ul>
1163
- {$for:foo as bar}<li>My colour is {$bar.baz}</li>{/for}
1164
- </ul>
1165
- `;
1166
- ```
1167
-
1168
- If/else statements are supported in templates, which include blocks based on the existence/truthiness of a property.
1140
+ In the following example, `missing` does not exist, so `test` is not substituted inside the loop, but `test` is still substituted outside the loop.
1169
1141
 
1170
1142
  ```html
1171
- {$if:is_on_earth}
1172
- Hello, world!
1173
- {else}
1174
- Hello, Mars!
1175
- {/if}
1143
+ <div>Hello {$test}!</div>
1144
+ {$for:missing}<div>Loop {$test}</div>{/for}
1176
1145
  ```
1177
1146
 
1178
- This also works with nested properties and loops.
1147
+ ```ts
1148
+ parse_template(..., {
1149
+ test: 'world'
1150
+ });
1151
+ ```
1179
1152
 
1180
1153
  ```html
1181
- {$for:countries as country}
1182
- {$if:country.is_in_europe}
1183
- Europe: {$country.name}
1184
- {else}
1185
- Not Europe: {$country.name}
1186
- {/if}
1187
- {/for}
1154
+ <div>Hello world!</div>
1155
+ {$for}Loop <div>{$test}</div>{/for}
1188
1156
  ```
1189
1157
 
1190
1158
  <a id="api-content-generate-hash-subs"></a>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "spooder",
3
3
  "type": "module",
4
- "version": "4.3.0",
4
+ "version": "4.3.1",
5
5
  "exports": {
6
6
  ".": {
7
7
  "bun": "./src/api.ts",
package/src/api.ts CHANGED
@@ -108,8 +108,8 @@ export async function safe(target_fn: Callable) {
108
108
  }
109
109
  }
110
110
 
111
- type ReplacerFn = (key: string) => string | Array<any> | Record<string, any> | undefined;
112
- type Replacements = Record<string, string | Array<any> | Record<string, any>> | ReplacerFn;
111
+ type ReplacerFn = (key: string) => string | Array<string> | undefined;
112
+ type Replacements = Record<string, string | Array<string>> | ReplacerFn;
113
113
 
114
114
  export function parse_template(template: string, replacements: Replacements, drop_missing = false): string {
115
115
  let result = '';
@@ -118,28 +118,6 @@ export function parse_template(template: string, replacements: Replacements, dro
118
118
 
119
119
  const is_replacer_fn = typeof replacements === 'function';
120
120
 
121
- function getValue(key: string): any {
122
- const var_parts = key.split('.');
123
- let value: any = is_replacer_fn ? replacements(var_parts[0]) : replacements[var_parts[0]];
124
-
125
- for (let j = 1; j < var_parts.length; j++) {
126
- if (value && typeof value === 'object') {
127
- value = value[var_parts[j]];
128
- } else {
129
- value = undefined;
130
- break;
131
- }
132
- }
133
- return value;
134
- }
135
-
136
- function parseBlock(content: string, local_replacements: Replacements): string {
137
- return parse_template(content, {
138
- ...replacements,
139
- ...local_replacements
140
- }, drop_missing);
141
- }
142
-
143
121
  const template_length = template.length;
144
122
  for (let i = 0; i < template_length; i++) {
145
123
  const char = template[i];
@@ -152,8 +130,9 @@ export function parse_template(template: string, replacements: Replacements, dro
152
130
  buffer_active = false;
153
131
 
154
132
  if (buffer.startsWith('for:')) {
155
- const [loop_key, loop_var] = buffer.substring(4).split(' as ');
156
- const loop_entries = getValue(loop_key);
133
+ const loop_key = buffer.substring(4);
134
+
135
+ const loop_entries = is_replacer_fn ? replacements(loop_key) : replacements[loop_key];
157
136
  const loop_content_start_index = i + 1;
158
137
  const loop_close_index = template.indexOf('{/for}', loop_content_start_index);
159
138
 
@@ -162,48 +141,19 @@ export function parse_template(template: string, replacements: Replacements, dro
162
141
  result += '{$' + buffer + '}';
163
142
  } else {
164
143
  const loop_content = template.substring(loop_content_start_index, loop_close_index);
165
- if (Array.isArray(loop_entries)) {
166
- for (const loop_entry of loop_entries)
167
- result += parseBlock(loop_content, { [loop_var]: loop_entry });
144
+ if (loop_entries !== undefined) {
145
+ for (const loop_entry of loop_entries) {
146
+ const inner_content = loop_content.replaceAll('%s', loop_entry);
147
+ result += parse_template(inner_content, replacements, drop_missing);
148
+ }
168
149
  } else {
169
150
  if (!drop_missing)
170
151
  result += '{$' + buffer + '}' + loop_content + '{/for}';
171
152
  }
172
- i = loop_close_index + 5; // Move past {/for}
173
- }
174
- } else if (buffer.startsWith('if:')) {
175
- const condition_key = buffer.substring(3);
176
- const condition_value = getValue(condition_key);
177
- const if_content_start_index = i + 1;
178
- const if_close_index = template.indexOf('{/if}', if_content_start_index);
179
-
180
- if (if_close_index === -1) {
181
- if (!drop_missing)
182
- result += '{$' + buffer + '}';
183
- } else {
184
- const if_content = template.substring(if_content_start_index, if_close_index);
185
- const else_index = if_content.indexOf('{else}');
186
-
187
- if (else_index === -1) {
188
- // No else block
189
- if (condition_value) {
190
- result += parseBlock(if_content, {});
191
- }
192
- } else {
193
- // Has else block
194
- const true_content = if_content.substring(0, else_index);
195
- const false_content = if_content.substring(else_index + 6); // +6 to move past {else}
196
-
197
- if (condition_value)
198
- result += parseBlock(true_content, {});
199
- else
200
- result += parseBlock(false_content, {});
201
- }
202
- i = if_close_index + 4; // Move past {/if}
153
+ i += loop_content.length + 6;
203
154
  }
204
155
  } else {
205
- const replacement = getValue(buffer);
206
-
156
+ const replacement = is_replacer_fn ? replacements(buffer) : replacements[buffer];
207
157
  if (replacement !== undefined)
208
158
  result += replacement;
209
159
  else if (!drop_missing)