express-hbs 4.0.1 → 4.1.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 +65 -0
- package/index.d.ts +146 -0
- package/lib/hbs.js +45 -12
- package/lib/resolver.js +38 -4
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -79,6 +79,36 @@ hbs.express4({
|
|
|
79
79
|
});
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
## TypeScript
|
|
83
|
+
|
|
84
|
+
Type declarations ship with the package (`index.d.ts`), so no `@types` package
|
|
85
|
+
is needed:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import hbs = require('express-hbs');
|
|
89
|
+
|
|
90
|
+
app.engine('hbs', hbs.express4({ partialsDir: __dirname + '/views/partials' }));
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Supporting types such as `hbs.Options` and `hbs.AsyncHelperCallback` are
|
|
94
|
+
available on the `hbs` namespace.
|
|
95
|
+
|
|
96
|
+
Async helpers come in two forms, `(context, cb)` and `(context, options, cb)`.
|
|
97
|
+
TypeScript infers parameter types for the first form only. For the second, add
|
|
98
|
+
`satisfies hbs.AsyncHelperWithOptions` so the parameters are inferred:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
hbs.registerAsyncHelper('lookup', function (context, options, cb) {
|
|
102
|
+
cb(options.hash.fallback ?? context);
|
|
103
|
+
} satisfies hbs.AsyncHelperWithOptions);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Promise helpers infer `context` and `options` without `satisfies`:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
hbs.registerPromiseHelper('lookup', async (context, options) => options.hash.fallback ?? context);
|
|
110
|
+
```
|
|
111
|
+
|
|
82
112
|
## Syntax
|
|
83
113
|
|
|
84
114
|
To mark where a layout should insert page content:
|
|
@@ -190,6 +220,41 @@ in markup
|
|
|
190
220
|
{{{readFile 'tos.txt'}}}
|
|
191
221
|
```
|
|
192
222
|
|
|
223
|
+
The value returned by an async helper is ignored; only `cb` delivers output.
|
|
224
|
+
|
|
225
|
+
### Promise helpers
|
|
226
|
+
|
|
227
|
+
`registerPromiseHelper` renders whatever the helper returns, waiting for it when
|
|
228
|
+
it is a promise, so `async` functions work directly. The helper is called like a
|
|
229
|
+
synchronous helper: template params first, then `options`.
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
hbs.registerPromiseHelper('readFile', async function(filename, options) {
|
|
233
|
+
var content = await fs.promises.readFile(path.join(viewsDir, filename), 'utf8');
|
|
234
|
+
return new hbs.SafeString(content);
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
in markup
|
|
239
|
+
```
|
|
240
|
+
{{readFile 'tos.txt'}}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
- A rejected promise or a thrown error fails the render and is passed to the
|
|
244
|
+
render callback.
|
|
245
|
+
- `null` and `undefined` render as an empty string.
|
|
246
|
+
- Strings are escaped in `{{ }}` and not in `{{{ }}}`; return `hbs.SafeString`
|
|
247
|
+
to skip escaping.
|
|
248
|
+
- The result is only available as output. Like async helpers, a promise helper
|
|
249
|
+
returns a placeholder that is replaced after rendering, so subexpressions and
|
|
250
|
+
block conditions such as `{{#if (enabled)}}` or `{{upper (readFile 'a')}}`
|
|
251
|
+
see the placeholder string, not the resolved value. Do that work inside the
|
|
252
|
+
helper instead.
|
|
253
|
+
|
|
254
|
+
Use `registerAsyncHelper` for callback-style helpers. It does not wait for a
|
|
255
|
+
returned promise, because an `async function (context, cb)` that calls `cb`
|
|
256
|
+
later returns a promise that resolves to `undefined` first.
|
|
257
|
+
|
|
193
258
|
|
|
194
259
|
## i18n support
|
|
195
260
|
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import Handlebars = require('handlebars');
|
|
2
|
+
|
|
3
|
+
declare namespace hbs {
|
|
4
|
+
type HandlebarsEnvironment = typeof Handlebars;
|
|
5
|
+
|
|
6
|
+
/** Compiled Handlebars template, tagged with the file it came from when known. */
|
|
7
|
+
type CompiledTemplate = Handlebars.TemplateDelegate & { __filename?: string };
|
|
8
|
+
|
|
9
|
+
/** Minimal shape of the `i18n` module used to register the `__` and `__n` helpers. */
|
|
10
|
+
interface I18n {
|
|
11
|
+
__(...args: any[]): string;
|
|
12
|
+
__n(...args: any[]): string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Options {
|
|
16
|
+
/** Path to partial templates; one directory or several. */
|
|
17
|
+
partialsDir?: string | string[];
|
|
18
|
+
/** Absolute path to a directory that layouts must reside in. */
|
|
19
|
+
restrictLayoutsTo?: string;
|
|
20
|
+
/** Name of the block helper. Defaults to `block`. */
|
|
21
|
+
blockHelperName?: string;
|
|
22
|
+
/** Name of the content helper. Defaults to `contentFor`. */
|
|
23
|
+
contentHelperName?: string;
|
|
24
|
+
/** Absolute path to the default layout template. */
|
|
25
|
+
defaultLayout?: string;
|
|
26
|
+
/** Extension for templates and partials. Defaults to `.hbs`. */
|
|
27
|
+
extname?: string;
|
|
28
|
+
/** Handlebars module to use instead of the bundled instance. */
|
|
29
|
+
handlebars?: HandlebarsEnvironment;
|
|
30
|
+
/** i18n object; registers the `__` and `__n` helpers. */
|
|
31
|
+
i18n?: I18n;
|
|
32
|
+
/** Path to layout templates. */
|
|
33
|
+
layoutsDir?: string;
|
|
34
|
+
/** Views directory used when not rendering through Express. */
|
|
35
|
+
viewsDir?: string | string[];
|
|
36
|
+
/** Options passed to every compiled template call. */
|
|
37
|
+
templateOptions?: Handlebars.RuntimeOptions;
|
|
38
|
+
/** Pretty-print rendered HTML with js-beautify. */
|
|
39
|
+
beautify?: boolean;
|
|
40
|
+
/** Override template compilation. */
|
|
41
|
+
onCompile?: (exhbs: ExpressHbs, source: string, filename?: string) => CompiledTemplate;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Locals passed to `res.render()` / the engine function. */
|
|
45
|
+
interface RenderOptions {
|
|
46
|
+
settings?: { views?: string | string[]; [key: string]: any };
|
|
47
|
+
cache?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Layout to render with, or a falsy value to render without any layout.
|
|
50
|
+
*
|
|
51
|
+
* Passing user-controlled data here can read arbitrary files unless
|
|
52
|
+
* `restrictLayoutsTo` is set.
|
|
53
|
+
*/
|
|
54
|
+
layout?: string | null | false | 0;
|
|
55
|
+
[key: string]: any;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type RenderCallback = (err: any, html?: string) => void;
|
|
59
|
+
|
|
60
|
+
/** Express-compatible view engine function. */
|
|
61
|
+
interface Engine {
|
|
62
|
+
(filename: string, options: RenderOptions, cb: RenderCallback): void;
|
|
63
|
+
(filename: string, source: string | null, options: RenderOptions, cb: RenderCallback): void;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
type AsyncHelperCallback = (result: any) => void;
|
|
67
|
+
|
|
68
|
+
type AsyncHelper = (this: any, context: any, cb: AsyncHelperCallback) => void;
|
|
69
|
+
|
|
70
|
+
type AsyncHelperWithOptions = (
|
|
71
|
+
this: any,
|
|
72
|
+
context: any,
|
|
73
|
+
options: Handlebars.HelperOptions,
|
|
74
|
+
cb: AsyncHelperCallback,
|
|
75
|
+
) => void;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Promise helper. Handlebars passes `(...params, options)`; this type covers
|
|
79
|
+
* the common one-param form so TypeScript can infer every parameter.
|
|
80
|
+
*/
|
|
81
|
+
type PromiseHelper = (this: any, context: any, options: Handlebars.HelperOptions) => unknown;
|
|
82
|
+
|
|
83
|
+
interface ExpressHbs {
|
|
84
|
+
handlebars: HandlebarsEnvironment;
|
|
85
|
+
SafeString: typeof Handlebars.SafeString;
|
|
86
|
+
Utils: typeof Handlebars.Utils;
|
|
87
|
+
|
|
88
|
+
express3(options?: Options): Engine;
|
|
89
|
+
express4(options?: Options): Engine;
|
|
90
|
+
|
|
91
|
+
registerHelper(name: string, fn: Handlebars.HelperDelegate): void;
|
|
92
|
+
registerHelper(helpers: Handlebars.HelperDeclareSpec): void;
|
|
93
|
+
/**
|
|
94
|
+
* Register a helper that delivers its result later via `cb(result)`. The
|
|
95
|
+
* helper receives `options` only when it declares three parameters.
|
|
96
|
+
*
|
|
97
|
+
* TypeScript infers parameter types for the `(context, cb)` form. For the
|
|
98
|
+
* `(context, options, cb)` form, add `satisfies hbs.AsyncHelperWithOptions`
|
|
99
|
+
* to the function so its parameters are inferred.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* hbs.registerAsyncHelper('readFile', function (filename, cb) {
|
|
103
|
+
* fs.readFile(filename, 'utf8', (err, content) => cb(content));
|
|
104
|
+
* });
|
|
105
|
+
*
|
|
106
|
+
* hbs.registerAsyncHelper('lookup', function (context, options, cb) {
|
|
107
|
+
* cb(options.hash.fallback ?? context);
|
|
108
|
+
* } satisfies hbs.AsyncHelperWithOptions);
|
|
109
|
+
*/
|
|
110
|
+
registerAsyncHelper(name: string, fn: AsyncHelper): void;
|
|
111
|
+
registerAsyncHelper(name: string, fn: AsyncHelperWithOptions): void;
|
|
112
|
+
/**
|
|
113
|
+
* Register a helper whose return value, or the value its promise resolves
|
|
114
|
+
* to, is rendered. A rejection or thrown error fails the render, and
|
|
115
|
+
* `null`/`undefined` render as an empty string. It is called like a sync
|
|
116
|
+
* helper, with the template params followed by `options`.
|
|
117
|
+
*
|
|
118
|
+
* The result is only usable as output: subexpressions and block conditions
|
|
119
|
+
* such as `{{#if (helper)}}` see a placeholder string, not the resolved value.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* hbs.registerPromiseHelper('readFile', async function (filename, options) {
|
|
123
|
+
* return new hbs.SafeString(await fs.promises.readFile(filename, 'utf8'));
|
|
124
|
+
* });
|
|
125
|
+
*/
|
|
126
|
+
registerPromiseHelper(name: string, fn: PromiseHelper): void;
|
|
127
|
+
registerPromiseHelper(name: string, fn: (this: any, ...args: any[]) => unknown): void;
|
|
128
|
+
registerPartial(name: string, source: string, filename?: string): void;
|
|
129
|
+
compile(source: string, filename?: string): CompiledTemplate;
|
|
130
|
+
|
|
131
|
+
getTemplateOptions(): Handlebars.RuntimeOptions;
|
|
132
|
+
updateTemplateOptions(templateOptions: Handlebars.RuntimeOptions): void;
|
|
133
|
+
getLocalTemplateOptions(locals: object): Handlebars.RuntimeOptions;
|
|
134
|
+
updateLocalTemplateOptions<T extends Handlebars.RuntimeOptions | undefined>(
|
|
135
|
+
locals: object,
|
|
136
|
+
localTemplateOptions: T,
|
|
137
|
+
): T;
|
|
138
|
+
|
|
139
|
+
/** Creates an isolated instance with its own cache and Handlebars environment. */
|
|
140
|
+
create(): ExpressHbs;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
declare const hbs: hbs.ExpressHbs;
|
|
145
|
+
|
|
146
|
+
export = hbs;
|
package/lib/hbs.js
CHANGED
|
@@ -348,7 +348,7 @@ ExpressHbs.prototype.compile = function (source, filename) {
|
|
|
348
348
|
if (typeof source !== 'string') {
|
|
349
349
|
throw new Error('registerPartial must be a string for empty comment workaround');
|
|
350
350
|
}
|
|
351
|
-
if (source.indexOf('}}') === source.length - 2) {
|
|
351
|
+
if (source.length >= 2 && source.indexOf('}}') === source.length - 2) {
|
|
352
352
|
source += ' ';
|
|
353
353
|
}
|
|
354
354
|
|
|
@@ -375,15 +375,20 @@ ExpressHbs.prototype.compile = function (source, filename) {
|
|
|
375
375
|
* @param {String} name The name of the partial as used in a template.
|
|
376
376
|
* @param {String} fn The `function(options, cb)`
|
|
377
377
|
*/
|
|
378
|
+
function getResolverCache(self, name, context, options) {
|
|
379
|
+
var resolverCache =
|
|
380
|
+
self.resolverCache ||
|
|
381
|
+
_.get(context, 'data.root.resolverCache') ||
|
|
382
|
+
_.get(options, 'data.root.resolverCache');
|
|
383
|
+
if (!resolverCache) {
|
|
384
|
+
throw new Error('Could not find resolver cache in async helper ' + name + '.');
|
|
385
|
+
}
|
|
386
|
+
return resolverCache;
|
|
387
|
+
}
|
|
388
|
+
|
|
378
389
|
ExpressHbs.prototype.registerAsyncHelper = function (name, fn) {
|
|
379
390
|
this.handlebars.registerHelper(name, function (context, options) {
|
|
380
|
-
var resolverCache =
|
|
381
|
-
this.resolverCache ||
|
|
382
|
-
_.get(context, 'data.root.resolverCache') ||
|
|
383
|
-
_.get(options, 'data.root.resolverCache');
|
|
384
|
-
if (!resolverCache) {
|
|
385
|
-
throw new Error('Could not find resolver cache in async helper ' + name + '.');
|
|
386
|
-
}
|
|
391
|
+
var resolverCache = getResolverCache(this, name, context, options);
|
|
387
392
|
if (options && fn.length > 2) {
|
|
388
393
|
var resolveFunc = function (arr, cb) {
|
|
389
394
|
return fn.call(this, arr[0], arr[1], cb);
|
|
@@ -395,6 +400,28 @@ ExpressHbs.prototype.registerAsyncHelper = function (name, fn) {
|
|
|
395
400
|
});
|
|
396
401
|
};
|
|
397
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Registers a helper that returns its result, or a promise for it.
|
|
405
|
+
*
|
|
406
|
+
* Unlike `registerAsyncHelper`, the returned value is used as the output, so
|
|
407
|
+
* `async` functions work directly. A rejection or thrown error fails the render.
|
|
408
|
+
*
|
|
409
|
+
* @param {String} name The name of the helper as used in a template.
|
|
410
|
+
* @param {Function} fn Called like a sync helper, `function(...params, options)`, and
|
|
411
|
+
* returns a value or a promise for one.
|
|
412
|
+
*/
|
|
413
|
+
ExpressHbs.prototype.registerPromiseHelper = function (name, fn) {
|
|
414
|
+
this.handlebars.registerHelper(name, function () {
|
|
415
|
+
var self = this;
|
|
416
|
+
var args = arguments;
|
|
417
|
+
var options = args[args.length - 1];
|
|
418
|
+
var resolverCache = getResolverCache(self, name, args[0], options);
|
|
419
|
+
return resolver.resolvePromise(resolverCache, function () {
|
|
420
|
+
return fn.apply(self, args);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
};
|
|
424
|
+
|
|
398
425
|
ExpressHbs.prototype.getTemplateOptions = function () {
|
|
399
426
|
return this._options.templateOptions;
|
|
400
427
|
};
|
|
@@ -459,7 +486,7 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
|
|
|
459
486
|
options.blockCache = {};
|
|
460
487
|
options.resolverCache = {};
|
|
461
488
|
|
|
462
|
-
this.viewsDir = options.settings.views || this.viewsDirOpt;
|
|
489
|
+
this.viewsDir = (options.settings && options.settings.views) || this.viewsDirOpt;
|
|
463
490
|
var self = this;
|
|
464
491
|
|
|
465
492
|
/**
|
|
@@ -643,10 +670,16 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
|
|
|
643
670
|
Object.keys(values).forEach(function (key) {
|
|
644
671
|
values[key] = replaceValue(values, values[key]);
|
|
645
672
|
});
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
673
|
+
var replaced = replaceValue(values, res);
|
|
674
|
+
// Re-check against the live cache: nested async helpers may have registered new IDs
|
|
675
|
+
// while resolving. Stop if a pass made no progress so unknown IDs can never loop forever.
|
|
676
|
+
if (
|
|
677
|
+
replaced !== res &&
|
|
678
|
+
resolver.hasResolvers(replaced, options.resolverCache, self.Utils.escapeExpression)
|
|
679
|
+
) {
|
|
680
|
+
return handleAsync(null, replaced);
|
|
649
681
|
}
|
|
682
|
+
res = replaced;
|
|
650
683
|
cb(null, res);
|
|
651
684
|
});
|
|
652
685
|
}
|
package/lib/resolver.js
CHANGED
|
@@ -12,8 +12,12 @@ var ID_ESCAPED_STRING = '<_';
|
|
|
12
12
|
|
|
13
13
|
var ID_SUFFIX = '__';
|
|
14
14
|
|
|
15
|
+
function createId() {
|
|
16
|
+
return ID_PREFIX + ID_ESCAPED_STRING + generateId(ID_LENGTH) + ID_SUFFIX;
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
function resolve(cache, fn, context) {
|
|
16
|
-
var id =
|
|
20
|
+
var id = createId();
|
|
17
21
|
cache[id] = new Promise(function (passed, failed) {
|
|
18
22
|
try {
|
|
19
23
|
fn(context, function (res) {
|
|
@@ -26,6 +30,23 @@ function resolve(cache, fn, context) {
|
|
|
26
30
|
return id;
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
// Like resolve, but `fn` takes no callback: it returns the result or a thenable.
|
|
34
|
+
// A sync throw or a rejection fails the render. null/undefined render as an empty
|
|
35
|
+
// string, matching how Handlebars renders sync helpers.
|
|
36
|
+
function resolvePromise(cache, fn) {
|
|
37
|
+
var id = createId();
|
|
38
|
+
var promise = new Promise(function (passed) {
|
|
39
|
+
passed(fn());
|
|
40
|
+
}).then(function (res) {
|
|
41
|
+
return res === undefined || res === null ? '' : res;
|
|
42
|
+
});
|
|
43
|
+
// done() reports the rejection; this stops Node flagging it as unhandled when the
|
|
44
|
+
// render fails before done() attaches its handlers.
|
|
45
|
+
promise.catch(function () {});
|
|
46
|
+
cache[id] = promise;
|
|
47
|
+
return id;
|
|
48
|
+
}
|
|
49
|
+
|
|
29
50
|
// cache is an object where the keys are cache keys and values are promises. See above.
|
|
30
51
|
function done(cache, callback) {
|
|
31
52
|
return Promise.all(Object.values(cache))
|
|
@@ -38,22 +59,35 @@ function done(cache, callback) {
|
|
|
38
59
|
callback(null, resolvedCache);
|
|
39
60
|
})
|
|
40
61
|
.catch(function (error) {
|
|
62
|
+
// Callers use `if (err)`, so a falsy reason (e.g. `Promise.reject()`) would
|
|
63
|
+
// otherwise be treated as success with no values.
|
|
64
|
+
if (!error) {
|
|
65
|
+
error = new Error('Async helper failed with ' + String(error));
|
|
66
|
+
}
|
|
41
67
|
callback(error);
|
|
42
68
|
});
|
|
43
69
|
}
|
|
44
70
|
|
|
45
|
-
function hasResolvers(text) {
|
|
71
|
+
function hasResolvers(text, cache, escape) {
|
|
46
72
|
// NOTE: We specifically search the text for the ID_PREFIX **NOT** including the escapable character
|
|
47
73
|
// This is because that character can be escaped in the text, and lead us to not finding unresolved
|
|
48
74
|
// async helper outputs.
|
|
49
|
-
if (text.
|
|
75
|
+
if (text.indexOf(ID_PREFIX) < 0) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (!cache) {
|
|
50
79
|
return true;
|
|
51
80
|
}
|
|
52
|
-
|
|
81
|
+
// Only report IDs that were actually generated by an async helper. The prefix can also
|
|
82
|
+
// appear in user-supplied data, which must never be treated as an unresolved helper.
|
|
83
|
+
return Object.keys(cache).some(function (id) {
|
|
84
|
+
return text.indexOf(id) >= 0 || (escape && text.indexOf(escape(id)) >= 0);
|
|
85
|
+
});
|
|
53
86
|
}
|
|
54
87
|
|
|
55
88
|
module.exports = {
|
|
56
89
|
done: done,
|
|
57
90
|
hasResolvers: hasResolvers,
|
|
58
91
|
resolve: resolve,
|
|
92
|
+
resolvePromise: resolvePromise,
|
|
59
93
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-hbs",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Express handlebars template engine complete with multiple layouts, partials and blocks.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"express4",
|
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
"partials"
|
|
11
11
|
],
|
|
12
12
|
"main": "index.js",
|
|
13
|
+
"types": "index.d.ts",
|
|
13
14
|
"files": [
|
|
14
15
|
"index.js",
|
|
16
|
+
"index.d.ts",
|
|
15
17
|
"lib"
|
|
16
18
|
],
|
|
17
19
|
"engines": {
|
|
@@ -28,7 +30,9 @@
|
|
|
28
30
|
"author": "Mario Gutierrez <mario@barc.com>",
|
|
29
31
|
"license": "MIT",
|
|
30
32
|
"devDependencies": {
|
|
31
|
-
"@tryghost/pro-ship": "2.0.
|
|
33
|
+
"@tryghost/pro-ship": "2.0.4",
|
|
34
|
+
"@types/express": "5.0.6",
|
|
35
|
+
"@types/node": "26.6.6",
|
|
32
36
|
"cookie-parser": "1.4.7",
|
|
33
37
|
"express": "5.3.0",
|
|
34
38
|
"i18n": "0.15.4",
|
|
@@ -37,7 +41,8 @@
|
|
|
37
41
|
"oxfmt": "0.76.0",
|
|
38
42
|
"oxlint": "1.91.0",
|
|
39
43
|
"rewire": "9.0.1",
|
|
40
|
-
"supertest": "7.3.2"
|
|
44
|
+
"supertest": "7.3.2",
|
|
45
|
+
"typescript": "7.0.2"
|
|
41
46
|
},
|
|
42
47
|
"dependencies": {
|
|
43
48
|
"handlebars": "^4.7.10",
|
|
@@ -48,7 +53,8 @@
|
|
|
48
53
|
"js-beautify": "^2.0.0"
|
|
49
54
|
},
|
|
50
55
|
"scripts": {
|
|
51
|
-
"lint": "oxlint --quiet && oxfmt --check .",
|
|
56
|
+
"lint": "oxlint --quiet && oxfmt --check . && pnpm lint:types",
|
|
57
|
+
"lint:types": "tsc -p test/types",
|
|
52
58
|
"lint:fix": "oxlint --fix && oxfmt .",
|
|
53
59
|
"format": "oxfmt .",
|
|
54
60
|
"test": "mocha",
|