Skip to content

Commit d233e3e

Browse files
test: added test fixes
1 parent 485415e commit d233e3e

File tree

5 files changed

+131
-76
lines changed

5 files changed

+131
-76
lines changed

src/html.ts

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,55 @@
1-
import { Elysia } from 'elysia';
2-
import { isHtml } from './is-html';
3-
import { HtmlOptions } from './options';
1+
import { Elysia } from 'elysia'
2+
import { isHtml, isTagHtml } from './utils'
3+
import { HtmlOptions } from './options'
44

55
export function html(options: HtmlOptions = {}) {
6-
// Defaults
7-
options.contentType ??= 'text/html; charset=utf8';
8-
options.autoDetect ??= true;
9-
options.isHtml ??= isHtml;
10-
options.autoDoctype ??= true;
11-
12-
let instance = new Elysia({ name: '@elysiajs/html' }).derive(() => ({
13-
html(value: string) {
14-
if (options.autoDoctype && !isHtml(value)) {
15-
value = '<!doctype html>' + value;
16-
}
17-
18-
return new Response(value, {
19-
headers: { 'content-type': options.contentType! }
20-
});
21-
}
22-
}));
23-
24-
if (options.autoDetect) {
25-
instance = instance.onAfterHandle(({ set }, response) => {
26-
if (typeof response === 'string' && isHtml(response)) {
27-
set.headers['content-type'] = options.contentType!;
28-
return new Response(response, set);
29-
}
30-
});
31-
}
32-
33-
return instance;
6+
// Defaults
7+
options.contentType ??= 'text/html; charset=utf8'
8+
options.autoDetect ??= true
9+
options.isHtml ??= isHtml
10+
options.autoDoctype ??= true
11+
12+
let instance = new Elysia({ name: '@elysiajs/html' }).derive(() => ({
13+
html(value: string) {
14+
if (
15+
options.autoDoctype &&
16+
isHtml(value) &&
17+
// Avoids double adding !doctype or adding to non root html tags.
18+
isTagHtml(value)
19+
) {
20+
value = '<!doctype html>' + value
21+
}
22+
23+
return new Response(value, {
24+
headers: { 'content-type': options.contentType! }
25+
})
26+
}
27+
}))
28+
29+
if (options.autoDetect) {
30+
instance = instance.onAfterHandle(
31+
// onAfterHandle should be present on a lot of stack traces, so we should not
32+
// use anonymous functions here.
33+
function htmlHandle({ set }, response) {
34+
if (!isHtml(response)) {
35+
return
36+
}
37+
38+
// Full means that we should only try to convert raw string responses
39+
if (options.autoDoctype === 'full' && isTagHtml(response)) {
40+
response = '<!doctype html>' + response
41+
}
42+
43+
set.headers['content-type'] = options.contentType!
44+
45+
return new Response(
46+
// @ts-expect-error - We know this is a string.
47+
response,
48+
set
49+
)
50+
}
51+
)
52+
}
53+
54+
return instance
3455
}

src/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
export * from './html';
2-
export * from './options';
3-
export * from './is-html';
1+
// Adds @kitajs/html's Html globally.
2+
import '@kitajs/html/register'
3+
4+
export * from './html'
5+
export * from './options'
6+
export * from './utils'
47

58
// We cannot use `export * as Html` because @kitajs/html uses `export =`
6-
export const Html = require('@kitajs/html') as typeof import('@kitajs/html');
9+
export const Html = require('@kitajs/html') as typeof import('@kitajs/html')

src/is-html.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/options.ts

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,48 @@
44
* @link https://elysiajs.com/plugins/html.html
55
*/
66
export interface HtmlOptions {
7-
/**
8-
* The content-type of the response.
9-
*
10-
* @default 'text/html; charset=utf8'
11-
*/
12-
contentType?: string;
7+
/**
8+
* The content-type of the response.
9+
*
10+
* @default 'text/html; charset=utf8'
11+
*/
12+
contentType?: string
1313

14-
/**
15-
* Whether to automatically detect HTML content and set the content-type.
16-
*
17-
* @default true
18-
*/
19-
autoDetect?: boolean;
14+
/**
15+
* Whether to automatically detect HTML content and set the content-type.
16+
*
17+
* @default true
18+
*/
19+
autoDetect?: boolean
2020

21-
/**
22-
* Whether to automatically add `<!doctype html>` to the response, if not found.
23-
*
24-
* @default true
25-
*/
26-
autoDoctype?: boolean;
21+
/**
22+
* Whether to automatically add `<!doctype html>` to a response starting with <html>, if not found.
23+
*
24+
* Use `full` to also automatically add doctypes on responses returned without this plugin
25+
*
26+
* ```ts
27+
* // without the plugin
28+
* app.get('/', () => '<html></html>')
29+
*
30+
* // With the plugin
31+
* app.get('/', ({ html }) => html('<html></html>')
32+
* ```
33+
*
34+
* @default true
35+
*/
36+
autoDoctype?: boolean | 'full'
2737

28-
/**
29-
* The function used to detect if a string is a html or not. Default
30-
* implementation if length is greater than 3, starts with `<` and ends
31-
* with `>`.
32-
*
33-
* There's no real way to validate HTML, so this is a best guess.
34-
*
35-
* @see https://stackoverflow.com/q/1732348
36-
* @see https://stackoverflow.com/q/11229831
37-
*
38-
* @default isHtml
39-
*/
40-
isHtml?: (this: void, value: string) => boolean;
38+
/**
39+
* The function used to detect if a string is a html or not. Default
40+
* implementation if length is greater than 3, starts with `<` and ends
41+
* with `>`.
42+
*
43+
* There's no real way to validate HTML, so this is a best guess.
44+
*
45+
* @see https://stackoverflow.com/q/1732348
46+
* @see https://stackoverflow.com/q/11229831
47+
*
48+
* @default isHtml
49+
*/
50+
isHtml?: (this: void, value: string) => boolean
4151
}

src/utils.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* There's no real way to validate HTML, so this is a best guess.
3+
*
4+
* @see https://stackoverflow.com/q/1732348
5+
* @see https://stackoverflow.com/q/11229831
6+
*/
7+
export function isHtml(this: void, value?: any): value is string {
8+
if (typeof value !== 'string') {
9+
return false
10+
}
11+
12+
value = value.trim()
13+
const length = value.length
14+
15+
return (
16+
// Minimum html is 7 characters long: <a></a>
17+
length >= 7 &&
18+
// open tag
19+
value[0] === '<' &&
20+
// close tag
21+
value[length - 1] === '>'
22+
)
23+
}
24+
25+
/**
26+
* Returns true if the string starts with `<html`, **ignores whitespaces and
27+
* casing**.
28+
*/
29+
export function isTagHtml(this: void, value: string) {
30+
return value.trimStart().slice(0, 5).startsWith('<html')
31+
}

0 commit comments

Comments
 (0)