Giter VIP home page Giter VIP logo

elysia-html's People

Contributors

arthurfiorette avatar bogeychan avatar brattonross avatar gtramontina avatar saltyaom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

elysia-html's Issues

Using `Bun.build` will result into errors when running the output.

If you build a project with bun using this package, you can't run the built file.

How to reproduce

  • Create a project using elysia and the elysia-html plugin.
  • Compile the project
bun build --target=bun ./src/lambda.ts --outfile=lambda.js
  • Try to run the project
bun lambda.js
  • This will result into
32 | var require_html = __commonJS((exports, module) => {
33 |   __export(exports, {
34 |     toKebabCase: () => {
35 |       {
36 | 
37 |         return $toKebabCase;
                  ^
ReferenceError: Can't find variable: $toKebabCase
      at toKebabCase (/Users/awilderink/code/bun-lambda/lambda.js:37:15)

This could also be an issue with bun build or @kitajs/html.

Returning html always return a HTTP 200 status code

When using this plugin the handler will always return HTTP 200 code even if status code is explicitly set

reproduction

export const app = new Elysia()
	.use(html())
	.get('/should-return-error', ({set}) => {
		set.status = 422;

		return <div></div>;
	})

it returns http code 200 instead of 422.

Most of the time it's fine. But sometimes (for example using htmx) you want to be able to return html with your error

Example doesn't work

I don't know if I am just missing the obvious here but I basically did exactly what the example showed on a fresh new project.

All the documentation shows is this example and so I don't know if anything new was introduced without updating docs or if it something broke between 0.5.2 and 0.6.0

This does not occur on version 0.5.2, where it all just works without any errors.

// Index.ts
import { Elysia } from 'elysia'
import { html } from '@elysiajs/html'

const page = `<!DOCTYPE HTML>
<html lang="en">
    <head>
        <title>Hello World</title>
    </head>
    <body>
        <h1>Hello World</h1>
    </body>
</html>`

const app = new Elysia()
    .use(html()) // <= Errors here
    .get('/', () => page)
    .get('/html', ({ html }) => html(page))
    .listen(8080)

I get the following error:

Argument of type 'Elysia<{ path: ""; store: {}; error: {}; request: { sanitize: typeof sanitize; html(value: string): Response; }; schema: {}; meta: { schema: {}; defs: {}; exposed: {}; }; }>' is not assignable to parameter of type 'MaybePromise<(app: Elysia<ElysiaInstance>) => MaybePromise<Elysia<any>>> | Promise<{ default: (elysia: Elysia<any>) => MaybePromise<...>; }>'

type errors for iframe attributes in tsx files

Adding iframe elements to a tsx file produces type errors for supported attributes like allow or frameborder.

"Type '{ width: string; height: string; src: string; title: string; frameborder: string; allow: string; allowfullscreen: true; }' is not assignable to type 'HtmlIFrameTag'.
Property 'frameborder' does not exist on type 'HtmlIFrameTag'."

Minimal reproduction:
Init new bun elysia project
bun create elysia app

Update tsconfig.json

// tsconfig.json
{
    "compilerOptions": {
        "jsx": "react",
        "jsxFactory": "Html.createElement",
        "jsxFragmentFactory": "Html.Fragment"
    }
}

Update index.tsx file:

import { Elysia } from 'elysia'
import { html } from '@elysiajs/html'

new Elysia()
    .use(html())
    .get('/jsx', () => (
        <html lang='en'>
            <head>
                <title>Hello World</title>
            </head>
            <body>
                <h1>Hello World</h1>
                <iframe width="560" height="315" src="https://www.youtube.com/embed/dQw4w9WgXcQ?si=RclVnENrgMEt3tD9" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
            </body>
        </html>
    ))
    .listen(3000)

Runtime error after just importing the module

Hi I've been through the setup phase of a basic server and when trying to add the html module it seems like it doesn't work.

Hello world program results in error after use(html())

TypeError: $.map is not a function. (In '$.map((X)=>{if(Z)X.$elysiaHookType="global";else X.$elysiaHookType=void 0;return X})', '$.map' is undefined)

turn on noUncheckedIndexedAccess TS rule

When setting noUncheckedIndexedAccess to true in a project consuming this library, type errors ensue:

node_modules/@elysiajs/cors/src/index.ts:211:49 - error TS2345: Argument of type 'Origin | undefined' is not assignable to parameter of type 'Origin'.
  Type 'undefined' is not assignable to type 'Origin'.

211                     const value = processOrigin(origins[i], request, from)
                                                    ~~~~~~~~~~

node_modules/@elysiajs/static/src/index.ts:107:34 - error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
  Type 'undefined' is not assignable to type 'string'.

107                 if (shouldIgnore(file)) continue
                                     ~~~~

node_modules/@elysiajs/static/src/index.ts:109:62 - error TS2769: No overload matches this call.
  Overload 1 of 3, '(path: string | URL, options?: BlobPropertyBag | undefined): BunFile', gave the following error.
    Argument of type 'string | undefined' is not assignable to parameter of type 'string | URL'.
      Type 'undefined' is not assignable to type 'string | URL'.
  Overload 2 of 3, '(path: Uint8Array | ArrayBufferLike, options?: BlobPropertyBag | undefined): BunFile', gave the following error.
    Argument of type 'string | undefined' is not assignable to parameter of type 'Uint8Array | ArrayBufferLike'.
      Type 'undefined' is not assignable to type 'Uint8Array | ArrayBufferLike'.
  Overload 3 of 3, '(fileDescriptor: number, options?: BlobPropertyBag | undefined): BunFile', gave the following error.
    Argument of type 'string | undefined' is not assignable to parameter of type 'number'.
      Type 'undefined' is not assignable to type 'number'.

109                 const response = () => new Response(Bun.file(file))
                                                                 ~~~~


node_modules/@elysiajs/static/src/index.ts:110:32 - error TS18048: 'file' is possibly 'undefined'.

110                 let fileName = file
                                   ~~~~

node_modules/@elysiajs/trpc/src/index.ts:19:17 - error TS2339: Property 'path' does not exist on type 'ValueError | undefined'.

19         const { path, message } = [...check.Errors(value)][0]
                   ~~~~

node_modules/@elysiajs/trpc/src/index.ts:19:23 - error TS2339: Property 'message' does not exist on type 'ValueError | undefined'.

19         const { path, message } = [...check.Errors(value)][0]
                         ~~~~~~~


Found 6 errors in 3 files.

Errors  Files
     1  node_modules/@elysiajs/cors/src/index.ts:211
     3  node_modules/@elysiajs/static/src/index.ts:107
     2  node_modules/@elysiajs/trpc/src/index.ts:19

`html()` is duplicating headers

when running this simple server

import { Elysia } from 'elysia'

import { html } from '@elysiajs/html';

const app = new Elysia()
  //@ts-ignore
  .use(html())
  //@ts-ignore
  .get('/', ({ set }) => {
    set.headers["test-header"] = "test-value"
    return "<div>hallo</div>"
  })
  //@ts-ignore
  .get('/error', ({ set, html }) => {
    set.headers["test-header"] = "test-value"
    return html("<div>hallo</div>")
  })
  .listen(3001)

console.log(`Elysia is running at ${app.server?.hostname}:${app.server?.port}`)

export type App = typeof app
console.log('Server is running on port 3001.')

I see duplicated test-header values on the /error endpoint, which is using html() whereas on the other endpoint which is not using the plugin the header looks good

grafik

I'm using most recent versions of all packages

/private/tmp/boo node_modules (34)
├── @elysiajs/[email protected]
├── [email protected]
├── [email protected]

Why is "react" required?

I haven't been able to get Elysia with JSX to work unless I also add react to the project. This seems undocumented. Is this expected behavior?

Send additional "set" response options

While adding a cookie to an html response. I found that the plugin does not add the additional response options. Instead it sets the content-type and that's it.
Is this intended?
Otherwise passing the set to the handleHtml handler which passes them to the response would solve the issue.
Any thoughts?

example returns pre-formatted string, not HTML

I'm trying out the following example of using the html plugin(0.7.3):

import { Elysia } from 'elysia'
import { html } from '@elysiajs/html' 

new Elysia()
    .use(html()) 
    .get('/', () => (
        <html lang="en">
            <head>
                <title>Hello World</title>
            </head>
            <body>
                <h1>Hello World</h1>
            </body>
        </html>
    ))
    .listen(3000)

The response header is content-type | text/plain;charset=utf-8, and the rendered HTML looks like the following:

image

The problem is solved when I switch back to 0.6.5.

Property 'html' does not exist on type 'Context<{ body: unknown; params: Record<never, string>; query: undefined; headers: undefined; response: unknown; }, {}> & {}'

a simple app following the documentation instructions. but still get err

index.tsx

import Elysia from "elysia";
import { html } from "@elysiajs/html";
import * as tHTML from "typed-html";
import staticPlugin from "@elysiajs/static";
import Home from "./components/Home";
import { About } from "./components/About";

new Elysia()
    .use(html())
    .use(staticPlugin())
    .get("/", ({ html }) =>
        html(
            <BaseHTML>
                <Home />
            </BaseHTML>
        )
    )
    .get("/about", ({ headers, html }) => {
        if (headers?.[`hx-request`]) {
            return <About />;
        }
        return html(
            <BaseHTML>
                <About />
            </BaseHTML>
        );
    })
    .listen(3000, ({ hostname, port }) =>
        console.log(`🦊 running on http://${hostname}:${port}`)
    );

const BaseHTML = ({ children }: tHTML.Children) => {
    return (
        <html lang="en">
            <head>
                <meta charset="UTF-8" />
                <meta
                    name="viewport"
                    content="width=device-width, initial-scale=1.0"
                />
                <title>FireBEH</title>
                <link rel="stylesheet" href="public/style.css" />
                <script src="public/htmx.min.js"></script>
            </head>
            <body class="px-4 grid grid-rows-[min-content,1fr]">
                <nav class="flex justify-between">
                    <h1 class="py-4">FireBEH🔥</h1>
                    <div class="flex items-center">
                        <button
                            hx-get="/about"
                            hx-target="#pageContent"
                            hx-push-url="/about"
                        >
                            About
                        </button>
                    </div>
                </nav>
                <div class="flex justify-center items-center" id="pageContent">
                    {children}
                </div>
            </body>
        </html>
    );
};

error:

[{
	"owner": "typescript",
	"code": "2339",
	"severity": 8,
	"message": "Property 'html' does not exist on type 'Context<{ body: unknown; params: Record<never, string>; query: undefined; headers: undefined; response: unknown; }, {}> & {}'.",
	"source": "ts",
	"startLineNumber": 11,
	"startColumn": 18,
	"endLineNumber": 11,
	"endColumn": 22
}]

tsc fails when used in an app that has tsconfig set to use the DOM lib on bun-types >= 1.0.19

Hello!

Part of my CI is to run tsc/bunx --bun tsc --noemit for static analysis test.
This test started to trip once we started using elysia-html

bunx --bun tsc --noemit
../../node_modules/@elysiajs/html/src/handler.ts:42:4 - error TS2345: Argument of type 'TransformStream<any, any>' is not assignable to parameter of type 'ReadableWritablePair<any, any>'.
  Types of property 'readable' are incompatible.
    Type 'ReadableStream<any>' is missing the following properties from type 'ReadableStream<any>': values, [Symbol.asyncIterator]

 42    new TransformStream({
       ~~~~~~~~~~~~~~~~~~~~~
 43     transform(chunk, controller) {
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
... 
 57     }
    ~~~~~
 58    })
    ~~~~~

../../node_modules/@elysiajs/html/src/handler.ts:63:3 - error TS2345: Argument of type 'ReadableStream<any>' is not assignable to parameter of type 'BodyInit | null | undefined'.
  Property 'prototype' is missing in type 'import("stream/web").ReadableStream<any>' but required in type 'ReadableStream<any>'.

63   stream,
     ~~~~~~

  ../../node_modules/typescript/lib/lib.dom.d.ts:18507:5
    18507     prototype: ReadableStream;
              ~~~~~~~~~
    'prototype' is declared here.


Found 2 errors in the same file, starting at: ../../node_modules/@elysiajs/html/src/handler.ts:42

The reason is that the TransformStream being used for the test is the one from DOM and not from the NodeJS/Bun Types

sanitize feature is implemented incorrectly

DOMPurify, when run on a server, requires an external DOM implementation such has JSDOM. The current initialization of sanitize will always be undefined for that reason. Correct usage:

import createDOMPurify from 'dompurify'
import {JSDOM} from 'jsdom'

const {sanitize} = createDOMPurify(new JSDOM('').window)

type for children

I have this bit of code that is working fine. What TypeScript type should use for children and where do I get the type definition?

const BaseHtml = ({children}) => (
  <html>
    <head>
      <title>HTMX Active Search</title>
      <script src="https://unpkg.com/[email protected]"></script>
      <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="p-8">{children}</body>
  </html>
);

html() loses earlier cookie() headers

Thehtml() plugin appears to drop headers that have been set earlier in the call lifecycle. Consider the following code where a derive() plugin is used to set a session cookie on all incoming requests:

import { Elysia } from 'elysia'
import { html } from '@elysiajs/html';
import { cookie } from '@elysiajs/cookie';

const app = new Elysia()
  .use(cookie())
  .use(html())
  .derive(({ setCookie }) => {
    const sessionID = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
    setCookie("session", sessionID, {
      httpOnly: true,
      sameSite: "lax"
    })
    return { sessionID }
  })
  .get('/', ({ sessionID, html }) => {
    return html(`<html><body><div>SessionID: ${sessionID}</div></body></html>`)
  })
  .get('/cookie-ok', ({ sessionID, set }) => {
    set.headers["content-type"] = "text/html; charset=utf8"
    return `<html><body><div>SessionID: ${sessionID}</div></body></html>`
  })
  .listen(3001)

console.log(`Elysia is running at ${app.server?.hostname}:${app.server?.port}`)

Requests to / do not return the cookie:

> curl http://localhost:3001/ -v
*   Trying 127.0.0.1:3001...
* Connected to localhost (127.0.0.1) port 3001 (#0)
> GET / HTTP/1.1
> Host: localhost:3001
> User-Agent: curl/8.1.2
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=utf8
< Date: Fri, 15 Sep 2023 09:13:29 GMT
< Content-Length: 70
<
* Connection #0 to host localhost left intact
<html><body><div>SessionID: gcjg4c01blszjs3fiirnhc</div></body></html>

Calls to the second endpoint do return the cookie:

> curl http://localhost:3001/cookie-ok -v
*   Trying 127.0.0.1:3001...
* Connected to localhost (127.0.0.1) port 3001 (#0)
> GET /cookie-ok HTTP/1.1
> Host: localhost:3001
> User-Agent: curl/8.1.2
> Accept: */*
>
< HTTP/1.1 200 OK
< set-cookie: session=mer7j274vpibxf6j3d94nd; Path=/; HttpOnly; SameSite=Lax
< Content-Type: text/html; charset=utf8
< Date: Fri, 15 Sep 2023 09:14:22 GMT
< Content-Length: 70
<
* Connection #0 to host localhost left intact
<html><body><div>SessionID: mer7j274vpibxf6j3d94nd</div></body></html>

Looking at the code, the plugin simply returns a new Response object without merging the existing headers from the set parameters.

https://github.com/elysiajs/elysia-html/blob/main/src/index.ts#L18-L24

Something like this works well:

    html(value: string) {
      const headers: Record<string, string> = {
        "content-type": "text/html; charset=utf8",
      }
      Object.entries(set.headers).forEach(([k, v]) => {
        if (Array.isArray(v)) {
          headers[k] = v.join(" ")
        } else {
          headers[k] = v
        }
      })
      return new Response(value, {
        headers: headers,
      })

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.