minijinja-js 3.0.0-alpha.2 → 3.0.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 CHANGED
@@ -88,29 +88,206 @@ const result = env.evalExpr('1 + 1', {});
88
88
  console.log(result);
89
89
  ```
90
90
 
91
+ Register filters, tests and functions:
92
+
93
+ ```typescript
94
+ import { Environment } from "minijinja-js";
95
+
96
+ const env = new Environment();
97
+ env.addFilter("repeat", (value, times, { sep = "" } = {}) =>
98
+ Array(times).fill(value).join(sep)
99
+ );
100
+ env.addGlobal("double", (x) => x * 2);
101
+ console.log(env.renderStr("{{ 'ab'|repeat(3, sep='-') }} {{ double(21) }}"));
102
+ // -> ab-ab-ab 42
103
+ ```
104
+
105
+ Keyword arguments are passed to JavaScript callbacks as a trailing object.
106
+ Exceptions thrown by callbacks fail the render with an error whose `cause`
107
+ is the original exception. Throw a `TemplateError` to fail with a specific
108
+ error kind and message:
109
+
110
+ ```typescript
111
+ import { TemplateError } from "minijinja-js";
112
+
113
+ env.addFilter("sqrt", (value) => {
114
+ if (value < 0) {
115
+ throw new TemplateError("value must not be negative", { kind: "InvalidOperation" });
116
+ }
117
+ return Math.sqrt(value);
118
+ });
119
+ ```
120
+
121
+ Callbacks wrapped with `passState` receive the engine state as first
122
+ argument. It gives access to variables (`lookup`), the template name, the
123
+ auto escape mode, and allows applying filters and tests. The state is only
124
+ valid while the callback runs:
125
+
126
+ ```typescript
127
+ import { Environment, passState } from "minijinja-js";
128
+
129
+ const env = new Environment();
130
+ env.addFilter(
131
+ "greet",
132
+ passState((state, name) => `${state.lookup("greeting") ?? "Hello"} ${name}!`)
133
+ );
134
+ console.log(env.renderStr("{{ 'World'|greet }}", { greeting: "Hi" }));
135
+ // -> Hi World!
136
+ ```
137
+
138
+ The `random` filter as well as the `randrange` and `lipsum` functions are
139
+ available. Their output can be made deterministic with the `RAND_SEED` global.
140
+
141
+ ## Dates and Times
142
+
143
+ Date and time filters can be enabled from `minijinja-js/datetime`. They
144
+ mirror the `datetimeformat`, `dateformat` and `timeformat` filters and the
145
+ `now()` function from MiniJinja's contrib package but use the time zone
146
+ support of the JavaScript runtime:
147
+
148
+ ```typescript
149
+ import { Environment } from "minijinja-js";
150
+ import { addDateTimeSupport } from "minijinja-js/datetime";
151
+
152
+ const env = new Environment();
153
+ addDateTimeSupport(env);
154
+ env.addGlobal("TIMEZONE", "Europe/Vienna");
155
+ console.log(env.renderStr("{{ ts|datetimeformat(format='full') }}", { ts: 1687624642 }));
156
+ // -> Saturday, June 24 2023 18:37:22.0
157
+ ```
158
+
159
+ The filters accept Unix timestamps, ISO 8601 strings (optionally with a time
160
+ zone annotation such as `2023-06-24T16:37:22Z[Europe/Vienna]`) and `Date`
161
+ objects. The format is one of `short`, `medium` (the default), `long`,
162
+ `full`, `iso` and `unix` or a `strftime` style format string. The `format`
163
+ and `tz` keyword arguments default to the `DATETIME_FORMAT`, `DATE_FORMAT`,
164
+ `TIME_FORMAT` and `TIMEZONE` globals.
165
+
166
+ ## Values
167
+
168
+ Values passed to templates are converted as follows:
169
+
170
+ * `undefined` is undefined and `null` is none.
171
+ * Numbers that are safe integers become integers, all others floats.
172
+ `BigInt`s become integers.
173
+ * Arrays, `Set`s and typed arrays become sequences, `Uint8Array` and
174
+ `ArrayBuffer` become bytes.
175
+ * Plain objects and `Map`s become maps. Key order is preserved.
176
+ * `Date`s become ISO 8601 strings.
177
+ * Functions become callable.
178
+ * Other objects (for instance class instances) are not copied but accessed
179
+ lazily. This means getters work and methods are invoked with the object
180
+ as `this`.
181
+
182
+ Values returned to JavaScript (from `evalExpr` or as arguments to
183
+ callbacks) are converted back:
184
+
185
+ * undefined becomes `undefined`, none becomes `null`.
186
+ * Integers outside of the safe integer range become `BigInt`s.
187
+ * Sequences become arrays, bytes become `Uint8Array`s.
188
+ * Maps with only string keys become plain objects, other maps become `Map`s.
189
+ * Functions and objects that came from JavaScript are passed back unchanged.
190
+
91
191
  MiniJinja tuples retain tuple rendering inside templates. Expression results are
92
192
  returned as JavaScript arrays because JavaScript has no distinct tuple type.
93
193
 
94
- ## Web Usage
194
+ ## Configuration
95
195
 
96
- If you want to use minijinja-js from the browser instead of node, you will
97
- need to use slightly different imports and call init explicitly:
196
+ ```typescript
197
+ import { Environment } from "minijinja-js";
198
+
199
+ const env = new Environment();
200
+
201
+ // whitespace handling and undefined behavior
202
+ env.trimBlocks = true;
203
+ env.lstripBlocks = true;
204
+ env.undefinedBehavior = "strict";
205
+
206
+ // custom delimiters and line statements (unspecified ones use the defaults)
207
+ env.syntax = { variableStart: "${", variableEnd: "}", lineStatementPrefix: "#" };
208
+
209
+ // auto escaping per template name: "html", "json", "none", true or false
210
+ env.setAutoEscapeCallback((name) => name.endsWith(".html"));
211
+
212
+ // customize values before they are rendered (return undefined to keep them)
213
+ env.setFinalizer((value) => (value === null ? "" : undefined));
214
+
215
+ // limit the amount of work a template can do
216
+ env.fuel = 50000;
217
+
218
+ // Python compatible methods such as dict.items()
219
+ env.pycompat = true;
220
+ ```
221
+
222
+ Find the variables a template needs:
223
+
224
+ ```typescript
225
+ env.addTemplate("index.html", "{% set greeting = 'Hello' %}{{ greeting }} {{ user.name }}");
226
+ env.undeclaredVariablesInTemplate("index.html"); // ["user"]
227
+ env.undeclaredVariablesInTemplate("index.html", true); // ["user.name"]
228
+ ```
98
229
 
230
+ ## Safe Strings
231
+
232
+ Strings marked as safe are not auto escaped. Safe strings are represented
233
+ by `SafeString` (which extends `String`) and can be created with `markSafe`.
234
+ Callbacks receive safe strings (for instance after the `|safe` filter) as
235
+ `SafeString` objects and can return them to emit markup:
236
+
237
+ ```typescript
238
+ import { Environment, markSafe } from "minijinja-js";
239
+
240
+ const env = new Environment();
241
+ env.addFilter("bold", (value) => markSafe(`<b>${value}</b>`));
242
+ env.addTemplate("index.html", "{{ name|bold }} {{ icon }}");
243
+ console.log(env.renderTemplate("index.html", { name: "World", icon: markSafe("<i>*</i>") }));
244
+ // -> <b>World</b> <i>*</i>
245
+ ```
246
+
247
+ ## Errors
248
+
249
+ Errors are raised as `TemplateError` which extends `Error` and provides
250
+ `kind`, `detail`, `templateName`, `line`, `range` and `templateSource`
251
+ properties. If the error was caused by an exception thrown in a callback,
252
+ the exception is available as `cause`.
253
+
254
+ ## Runtimes
255
+
256
+ The package is an ES module and works in Node.js, Deno, Bun, browsers and
257
+ bundlers:
258
+
259
+ * In Node.js the wasm module is loaded synchronously. Recent Node.js versions
260
+ can also load the package with `require()`.
261
+ * In all other environments the wasm module is loaded relative to the package
262
+ (via `import.meta.url`) with top-level await.
263
+
264
+ If you need to control how the wasm module is loaded (for instance on
265
+ Cloudflare Workers or with a custom asset pipeline), use the `minijinja-js/init`
266
+ entry point and initialize the module yourself before use:
99
267
 
100
268
  ```javascript
101
- import init, { Environment } from "minijinja-js/dist/web";
102
- await init();
269
+ import { init, initSync, Environment } from "minijinja-js/init";
270
+
271
+ // load from a URL, Response or compiled module
272
+ await init(new URL("minijinja_js_bg.wasm", someBaseUrl));
273
+
274
+ // or synchronously from bytes or a compiled module
275
+ initSync(wasmModule);
103
276
  ```
104
277
 
278
+ The wasm file is exported as `minijinja-js/minijinja_js_bg.wasm`.
279
+
105
280
  ## Known Limitations
106
281
 
107
282
  There are various limitations with the binding today, some of which can be fixed,
108
283
  others probably not so much. You might run into the following:
109
284
 
110
- * Access of the template engine state from JavaScript is not possible.
111
- * You cannot register a custom auto escape callback or a finalizer
285
+ * Filters, tests and functions cannot be async.
112
286
  * The loader is synchronous; use sync I/O in Node etc... (e.g. `fs.readFileSync`)
113
- * If the engine panics, the WASM runtime corrupts.
287
+ * The environment cannot be modified while it renders (for instance from
288
+ within a filter).
289
+ * If the engine panics, the WASM runtime corrupts. This should not happen
290
+ but if it does, please report it as a bug.
114
291
 
115
292
  ## Sponsor
116
293
 
@@ -120,5 +297,5 @@ sponsor](https://github.com/sponsors/mitsuhiko).
120
297
  ## License and Links
121
298
 
122
299
  - [Issue Tracker](https://github.com/mitsuhiko/minijinja/issues)
123
- - [MiniJinja Playground](https://mitsuhiko.github.io/minijinja-playground/)
300
+ - [MiniJinja Playground](https://mitsuhiko.github.io/minijinja/)
124
301
  - License: [Apache-2.0](https://github.com/mitsuhiko/minijinja/blob/main/LICENSE)
@@ -0,0 +1,16 @@
1
+ import type { Environment } from "./types.js";
2
+
3
+ /**
4
+ * Registers the date and time filters and functions with an environment.
5
+ *
6
+ * This adds the `datetimeformat`, `dateformat` and `timeformat` filters and
7
+ * the `now` function. They behave like the ones of the `datetime` feature
8
+ * of minijinja-contrib but use the time zone support of the JavaScript
9
+ * runtime (`Intl`).
10
+ *
11
+ * The filters accept Unix timestamps (seconds), ISO 8601 strings
12
+ * (optionally with a time zone annotation such as `[Europe/Vienna]`) and
13
+ * `Date` objects. The `format` and `tz` keyword arguments default to the
14
+ * `DATETIME_FORMAT`, `DATE_FORMAT`, `TIME_FORMAT` and `TIMEZONE` globals.
15
+ */
16
+ export declare function addDateTimeSupport(env: Environment): void;