spooder 4.2.18 → 4.3.0
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/README.md +52 -18
- package/package.json +1 -1
- package/src/api.ts +62 -12
package/README.md
CHANGED
|
@@ -200,7 +200,9 @@ When starting or restarting a server process, `spooder` can automatically update
|
|
|
200
200
|
{
|
|
201
201
|
"spooder": {
|
|
202
202
|
"update": [
|
|
203
|
-
"git
|
|
203
|
+
"git reset --hard",
|
|
204
|
+
"git clean -fd",
|
|
205
|
+
"git pull origin main",
|
|
204
206
|
"bun install"
|
|
205
207
|
]
|
|
206
208
|
}
|
|
@@ -210,7 +212,7 @@ When starting or restarting a server process, `spooder` can automatically update
|
|
|
210
212
|
Each command should be a separate entry in the array and will be executed in sequence. The server process will be started once all commands have resolved.
|
|
211
213
|
|
|
212
214
|
> [!IMPORTANT]
|
|
213
|
-
>
|
|
215
|
+
> Chaining commands using `&&` or `||` operators does not work.
|
|
214
216
|
|
|
215
217
|
If a command in the sequence fails, the remaining commands will not be executed, however the server will still be started. This is preferred over entering a restart loop or failing to start the server at all.
|
|
216
218
|
|
|
@@ -435,7 +437,7 @@ In addition to the information provided by the developer, `spooder` also include
|
|
|
435
437
|
"modules": "108"
|
|
436
438
|
},
|
|
437
439
|
"bun": {
|
|
438
|
-
"version": "0.6.
|
|
440
|
+
"version": "0.6.5",
|
|
439
441
|
"rev": "f02561530fda1ee9396f51c8bc99b38716e38296",
|
|
440
442
|
"memory_usage": {
|
|
441
443
|
"rss": 99672064,
|
|
@@ -778,7 +780,7 @@ function default_directory_handler(file_path: string, file: BunFile, stat: DirSt
|
|
|
778
780
|
```
|
|
779
781
|
|
|
780
782
|
> [!NOTE]
|
|
781
|
-
> Uncaught `ENOENT` errors
|
|
783
|
+
> Uncaught `ENOENT` errors thrown from the directory handler will return a `404` response, other errors will return a `500` response.
|
|
782
784
|
|
|
783
785
|
> [!NOTE]
|
|
784
786
|
> The call to `apply_range` in the default directory handler will automatically slice the file based on the [`Range`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range) header. This function is also exposed as part of the `spooder` API for use in your own handlers.
|
|
@@ -1064,6 +1066,24 @@ const html = parse_template(template, replacements);
|
|
|
1064
1066
|
</html>
|
|
1065
1067
|
```
|
|
1066
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
|
+
|
|
1067
1087
|
By default, placeholders that do not appear in the replacement object will be left as-is. Set `drop_missing` to `true` to remove them.
|
|
1068
1088
|
|
|
1069
1089
|
```ts
|
|
@@ -1105,16 +1125,15 @@ parse_template('Hello {$world}', replacer);
|
|
|
1105
1125
|
</body>
|
|
1106
1126
|
</html>
|
|
1107
1127
|
```
|
|
1108
|
-
|
|
1109
1128
|
`parse_template` supports looping arrays with the following syntax.
|
|
1110
1129
|
|
|
1111
1130
|
```html
|
|
1112
|
-
{$for:foo}My colour is
|
|
1131
|
+
{$for:foo as bar}My colour is {$bar}{/for}
|
|
1113
1132
|
```
|
|
1114
1133
|
```ts
|
|
1115
1134
|
const template = `
|
|
1116
1135
|
<ul>
|
|
1117
|
-
{$for:foo}<li
|
|
1136
|
+
{$for:foo as bar}<li>{$bar}</li>{/for}
|
|
1118
1137
|
</ul>
|
|
1119
1138
|
`;
|
|
1120
1139
|
|
|
@@ -1133,24 +1152,39 @@ const html = parse_template(template, replacements);
|
|
|
1133
1152
|
</ul>
|
|
1134
1153
|
```
|
|
1135
1154
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
In the following example, `missing` does not exist, so `test` is not substituted inside the loop, but `test` is still substituted outside the loop.
|
|
1155
|
+
Loops also support object properties using the dot syntax.
|
|
1139
1156
|
|
|
1140
1157
|
```html
|
|
1141
|
-
|
|
1142
|
-
{$for:missing}<div>Loop {$test}</div>{/for}
|
|
1158
|
+
{$for:foo as bar}My colour is {$bar.baz}{/for}
|
|
1143
1159
|
```
|
|
1144
|
-
|
|
1145
1160
|
```ts
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
}
|
|
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.
|
|
1169
|
+
|
|
1170
|
+
```html
|
|
1171
|
+
{$if:is_on_earth}
|
|
1172
|
+
Hello, world!
|
|
1173
|
+
{else}
|
|
1174
|
+
Hello, Mars!
|
|
1175
|
+
{/if}
|
|
1149
1176
|
```
|
|
1150
1177
|
|
|
1178
|
+
This also works with nested properties and loops.
|
|
1179
|
+
|
|
1151
1180
|
```html
|
|
1152
|
-
|
|
1153
|
-
{$
|
|
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
1188
|
```
|
|
1155
1189
|
|
|
1156
1190
|
<a id="api-content-generate-hash-subs"></a>
|
package/package.json
CHANGED
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<string> | undefined;
|
|
112
|
-
type Replacements = Record<string, string | Array<string>> | ReplacerFn;
|
|
111
|
+
type ReplacerFn = (key: string) => string | Array<any> | Record<string, any> | undefined;
|
|
112
|
+
type Replacements = Record<string, string | Array<any> | Record<string, any>> | ReplacerFn;
|
|
113
113
|
|
|
114
114
|
export function parse_template(template: string, replacements: Replacements, drop_missing = false): string {
|
|
115
115
|
let result = '';
|
|
@@ -118,6 +118,28 @@ 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
|
+
|
|
121
143
|
const template_length = template.length;
|
|
122
144
|
for (let i = 0; i < template_length; i++) {
|
|
123
145
|
const char = template[i];
|
|
@@ -130,9 +152,8 @@ export function parse_template(template: string, replacements: Replacements, dro
|
|
|
130
152
|
buffer_active = false;
|
|
131
153
|
|
|
132
154
|
if (buffer.startsWith('for:')) {
|
|
133
|
-
const loop_key = buffer.substring(4);
|
|
134
|
-
|
|
135
|
-
const loop_entries = is_replacer_fn ? replacements(loop_key) : replacements[loop_key];
|
|
155
|
+
const [loop_key, loop_var] = buffer.substring(4).split(' as ');
|
|
156
|
+
const loop_entries = getValue(loop_key);
|
|
136
157
|
const loop_content_start_index = i + 1;
|
|
137
158
|
const loop_close_index = template.indexOf('{/for}', loop_content_start_index);
|
|
138
159
|
|
|
@@ -141,19 +162,48 @@ export function parse_template(template: string, replacements: Replacements, dro
|
|
|
141
162
|
result += '{$' + buffer + '}';
|
|
142
163
|
} else {
|
|
143
164
|
const loop_content = template.substring(loop_content_start_index, loop_close_index);
|
|
144
|
-
if (loop_entries
|
|
145
|
-
for (const loop_entry of loop_entries)
|
|
146
|
-
|
|
147
|
-
result += parse_template(inner_content, replacements, drop_missing);
|
|
148
|
-
}
|
|
165
|
+
if (Array.isArray(loop_entries)) {
|
|
166
|
+
for (const loop_entry of loop_entries)
|
|
167
|
+
result += parseBlock(loop_content, { [loop_var]: loop_entry });
|
|
149
168
|
} else {
|
|
150
169
|
if (!drop_missing)
|
|
151
170
|
result += '{$' + buffer + '}' + loop_content + '{/for}';
|
|
152
171
|
}
|
|
153
|
-
i
|
|
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}
|
|
154
203
|
}
|
|
155
204
|
} else {
|
|
156
|
-
const replacement =
|
|
205
|
+
const replacement = getValue(buffer);
|
|
206
|
+
|
|
157
207
|
if (replacement !== undefined)
|
|
158
208
|
result += replacement;
|
|
159
209
|
else if (!drop_missing)
|