├── .editorconfig ├── .github └── workflows │ ├── docs.yml │ └── test.yml ├── .gitignore ├── .prettierignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── dist └── .gitkeep ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── .nojekyll │ └── howto │ │ ├── intro.md │ │ └── xternal.md ├── docusaurus.config.js ├── package.json ├── pnpm-lock.yaml ├── sidebars.js ├── src │ ├── components │ │ └── HomepageFeatures │ │ │ ├── index.js │ │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.js │ │ └── index.module.css ├── static │ ├── .nojekyll │ ├── demo.gif │ └── img │ │ ├── docusaurus-social-card.jpg │ │ ├── docusaurus.png │ │ ├── favicon.ico │ │ ├── logo.svg │ │ ├── undraw_docusaurus_mountain.svg │ │ ├── undraw_docusaurus_react.svg │ │ └── undraw_docusaurus_tree.svg └── typedoc.json ├── example ├── README.md ├── package.json ├── pnpm-lock.yaml ├── src │ ├── index.jsx │ └── types.d.ts ├── tsconfig.hono.json └── tsconfig.json ├── mkdocs.yml ├── package.json ├── pnpm-lock.yaml ├── rules └── no-extensionless-relative-import.yml ├── scripts ├── dist.sh └── release.sh ├── sgconfig.yml ├── src ├── index.ts ├── jsx.d.ts └── typed-html │ ├── jsx-dev-runtime.ts │ └── jsx-runtime.ts ├── static └── demo.gif ├── tests └── static-jsx │ ├── jest.config.js │ ├── main.test.jsx │ ├── package.json │ ├── pnpm-lock.yaml │ └── tsconfig.json ├── tsconfig.esm.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = tab 3 | indent_size = 2 4 | max_line_length = 120 5 | 6 | [*.{md,yml,yaml}] 7 | indent_style = space 8 | indent_size = 2 9 | max_line_length = 80 10 | 11 | [*.{js,jsx,ts,tsx}] 12 | quote_type = double 13 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | push: 7 | branches: [main] 8 | 9 | concurrency: 10 | group: docs-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | defaults: 14 | run: 15 | working-directory: docs 16 | 17 | jobs: 18 | build: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v3 22 | - uses: pnpm/action-setup@v2 23 | with: 24 | version: 8 25 | - uses: actions/setup-node@v3 26 | with: 27 | cache: pnpm 28 | cache-dependency-path: docs/pnpm-lock.yaml 29 | 30 | - run: pnpm install 31 | working-directory: . 32 | - name: Build library 33 | run: pnpm dist 34 | working-directory: . 35 | 36 | - run: pnpm install 37 | - run: pnpm docusaurus generate-typedoc 38 | - name: Build website 39 | run: pnpm build 40 | - name: Upload artifacts 41 | uses: actions/upload-pages-artifact@v1 42 | with: 43 | path: docs/build 44 | deploy: 45 | needs: build 46 | permissions: 47 | pages: write 48 | id-token: write 49 | environment: 50 | name: github-pages 51 | url: ${{ steps.deployment.outputs.page_url }} 52 | runs-on: ubuntu-latest 53 | steps: 54 | - name: Deploy to GitHub Pages 55 | uses: actions/deploy-pages@v1 56 | id: deployment 57 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: tests 5 | 6 | on: 7 | push: 8 | branches: ["main"] 9 | paths-ignore: ["**.md"] 10 | pull_request: 11 | branches: ["main"] 12 | paths-ignore: ["**.md"] 13 | 14 | env: 15 | CARGO_TERM_COLOR: always 16 | 17 | jobs: 18 | static-jsx: 19 | concurrency: 20 | group: ci-test-static-jsx-${{ github.ref }}-${{ matrix.node-version }} 21 | cancel-in-progress: true 22 | runs-on: ubuntu-latest 23 | defaults: 24 | run: 25 | working-directory: tests/static-jsx 26 | 27 | strategy: 28 | matrix: 29 | node-version: [16.x, 18.x, 20.x] 30 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 31 | 32 | steps: 33 | - uses: actions/checkout@v3 34 | - uses: pnpm/action-setup@v2 35 | with: 36 | version: 8 37 | - name: Use Node.js ${{ matrix.node-version }} 38 | uses: actions/setup-node@v3 39 | with: 40 | node-version: ${{ matrix.node-version }} 41 | cache: "pnpm" 42 | cache-dependency-path: tests/static-jsx/pnpm-lock.yaml 43 | - run: pnpm install 44 | - name: Install dependencies (typed-htmx) 45 | run: pnpm install 46 | working-directory: . 47 | - name: Build library 48 | run: pnpm dist 49 | working-directory: . 50 | - run: pnpm test 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | docs/reference 4 | website 5 | .swc -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml 2 | docs -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.3.1 4 | 5 | - Fix bug introduced in last version, which disallowed ambient type imports 6 | - More completions for `hx-on-` and `hx-target` 7 | 8 | ## 0.3.0 9 | 10 | - Update definitions for htmx 1.9.10 11 | 12 | ## 0.2.3 13 | 14 | - Fix bug introduced in previous version that breaks the built-in HTML renderer 15 | 16 | ## 0.2.2 17 | 18 | - Fix ESM imports when not using bundlers 19 | 20 | ## 0.2.1 21 | 22 | - Fix types when not using typed-html 23 | 24 | ## 0.2.0 25 | 26 | - _(Breaking)_ `jsxConfig.jsonAttributes` changed to be a Set 27 | - New template function `html` for compatibility with swc-plugin-static-jsx 28 | 29 | ## 0.1.4 30 | 31 | - Do not doubly sanitize fragment children 32 | 33 | ## 0.1.3 34 | 35 | - Rename `config` to `jsxConfig`; imported directly from `typed-htmx` 36 | - When `jsxConfig.trusted` is false (default) and `jsxConfig.sanitize` is 37 | defined, plain text and interpolated values are sanitized 38 | - Slight reduction in performance, dependent on the sanitizer supplied 39 | 40 | ## 0.1.2 41 | 42 | - Fix `Fragment` not accepting non-array children and not sanitizing children 43 | - Increase jsx robustness against falsy values 44 | - Fix the value 0 not being rendered 45 | 46 | ## 0.1.1 47 | 48 | - Allow `jsx` to process arrays of children, unblocks Bun 49 | 50 | ## 0.1.0 51 | 52 | - Initialize package 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2023 Viet Dinh 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose 6 | with or without fee is hereby granted, provided that the above copyright notice 7 | and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 11 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 13 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 14 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 15 | THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # typed-htmx 2 | 3 | [![npm](https://img.shields.io/npm/v/typed-htmx?style=flat-square)](https://www.npmjs.com/package/typed-htmx) 4 | [![docs](https://github.com/Desdaemon/typed-htmx/actions/workflows/docs.yml/badge.svg)](https://github.com/Desdaemon/typed-htmx/actions/workflows/docs.yml) 5 | [![tests](https://github.com/Desdaemon/typed-htmx/actions/workflows/test.yml/badge.svg)](https://github.com/Desdaemon/typed-htmx/actions/workflows/test.yml) 6 | 7 | [![demo](static/demo.gif)](https://asciinema.org/a/598553) 8 | 9 | Definitions for htmx attributes in JSX. 10 | 11 | ## Usage 12 | 13 | You can configure `typed-htmx` either as pure type declarations, or as a JSX 14 | templating engine. 15 | 16 | ### As type declarations 17 | 18 | Configure your `tsconfig.json` as follows: 19 | 20 | ```jsonc 21 | { 22 | "compilerOptions": { 23 | "jsx": "react", 24 | "moduleResolution": "node16", // or "nodenext" 25 | "types": ["typed-htmx" /** and any other types you need */] 26 | } 27 | } 28 | ``` 29 | 30 | An alternative is to include a _[triple-slash directive]_ wherever you need 31 | completions for htmx attributes: 32 | 33 | ```jsx 34 | /// 35 | 36 | function MyComponent({ children }) { 37 | return
{children}
; 38 | // ^?: string | undefined 39 | } 40 | ``` 41 | 42 | If your frontend library injects its own JSX types, you'll need to augment it. 43 | See the [example project](https://github.com/Desdaemon/typed-htmx/tree/main/example) 44 | for a demo. typed-html and React are supported out of the box. 45 | 46 | ### As a JSX templating engine 47 | 48 | If you prefer to use JSX only for its templating capabilities in the vein of 49 | [typed-html], you can use `typed-htmx/typed-html` which is included with this 50 | library and optimized for htmx usage: 51 | 52 | - Attributes such as [`hx-vals`](https://htmx.org/attributes/hx-vals/) and 53 | [`hx-headers`](https://htmx.org/attributes/hx-headers/) may also accept an object 54 | literal, which will be stringified on demand. 55 | - Configurable options for sanitization, defaults to a no-op. 56 | 57 | Configure your `tsconfig.json` as follows: 58 | 59 | ```jsonc 60 | { 61 | "compilerOptions": { 62 | "jsx": "react-jsx", 63 | "jsxImportSource": "typed-htmx/typed-html", 64 | "moduleResolution": "node16" // or "nodenext" 65 | } 66 | } 67 | ``` 68 | 69 | [typed-html]: https://github.com/nicojs/typed-html 70 | [triple-slash directive]: https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html 71 | -------------------------------------------------------------------------------- /dist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desdaemon/typed-htmx/aa7f1336d5ee1a915c98329aba6b0e2debed132a/dist/.gitkeep -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | docs/api 11 | 12 | # Misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve("@docusaurus/core/lib/babel/preset")], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desdaemon/typed-htmx/aa7f1336d5ee1a915c98329aba6b0e2debed132a/docs/docs/.nojekyll -------------------------------------------------------------------------------- /docs/docs/howto/intro.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | ## Demo 4 | 5 | ```jsx twoslash 6 | // @errors: 2322 7 | 8 | function MyComponent({ children }) { 9 | return ( 10 | "" + 11 | ( 12 | 13 | 14 | My Website 15 | 16 | 17 |
18 | {children} 19 |
24 | 25 |
26 |
27 | 28 | 29 | ) 30 | ); 31 | } 32 | ``` 33 | 34 | ## Install 35 | 36 | ```shell 37 | npm i typed-htmx 38 | ``` 39 | 40 | You can also install as a dev dependency if you're only using the type definitions. 41 | 42 | ## Usage 43 | 44 | You can configure typed-htmx either as pure type declarations, or as a JSX 45 | templating engine. 46 | 47 | ### As type declarations 48 | 49 | Configure your `tsconfig.json` as follows: 50 | 51 | ```jsonc 52 | { 53 | "compilerOptions": { 54 | "jsx": "react", 55 | "moduleResolution": "node16", // or "nodenext" 56 | "types": ["typed-htmx" /** and any other types you need */] 57 | } 58 | } 59 | ``` 60 | 61 | An alternative is to include a _[triple-slash directive]_ wherever you need 62 | completions for htmx attributes: 63 | 64 | ```jsx twoslash 65 | /// 66 | 67 | function MyComponent({ children }) { 68 | return
{children}
; 69 | // ^? 70 | } 71 | ``` 72 | 73 | If your frontend library injects its own JSX types, you'll need to augment it. 74 | See the [example project](https://github.com/Desdaemon/typed-htmx/tree/main/example) 75 | for a demo. typed-html and React are supported out of the box. 76 | 77 | ### As a JSX templating engine 78 | 79 | If you prefer to use JSX only for its templating capabilities in the vein of 80 | [typed-html], you can use `typed-htmx/typed-html` which is included with this 81 | library and optimized for htmx usage: 82 | 83 | - Attributes such as [`hx-vals`](https://htmx.org/attributes/hx-vals/) and 84 | [`hx-headers`](https://htmx.org/attributes/hx-headers/) may also accept an object 85 | literal, which will be stringified on demand. 86 | - Configurable [options](#configuring-the-jsx-runtime) for sanitization, defaults to a no-op. 87 | 88 | Configure your `tsconfig.json` as follows: 89 | 90 | ```jsonc 91 | { 92 | "compilerOptions": { 93 | "jsx": "react-jsx", 94 | "jsxImportSource": "typed-htmx/typed-html", 95 | "moduleResolution": "node16" // or "nodenext" 96 | } 97 | } 98 | ``` 99 | 100 | ## Tips 101 | 102 | ### Configuring the JSX runtime 103 | 104 | If you don't have any other JSX runtimes like React or Preact set up, you can use 105 | `typed-htmx/typed-html`, which will convert JSX into strings at runtime. 106 | You can configure the runtime using [`jsxConfig`](/typed-htmx/docs/api/index/variables/jsxConfig): 107 | 108 | ```js twoslash 109 | import { jsxConfig } from "typed-htmx"; 110 | // Set to true to allow all text and skip sanitization 111 | jsxConfig.trusted = true; 112 | // Bring your own sanitizer 113 | jsxConfig.sanitize = (raw, originalType) => `..`; 114 | ``` 115 | 116 | ### Compiling JSX templates 117 | 118 | JSX functions are fairly fast and will unlikely to be a bottleneck in your server. 119 | However, it is possible to achieve higher performance via techniques such as code transformations à la Babel. 120 | For example, you can use [`swc-plugin-static-jsx`](https://github.com/Desdaemon/swc-plugin-static-jsx) 121 | which will transform the demo snippet into pure string interpolation: 122 | 123 | ```ts twoslash 124 | // Use typed-htmx's template function 125 | import { html } from "typed-htmx"; 126 | 127 | // Or provide your own 128 | function myHtml(raw: TemplateStringsArray, ...args: unknown[]): string { 129 | return `..`; 130 | } 131 | 132 | function MyComponent({ children }) { 133 | return ( 134 | "" + 135 | html` 136 | 137 | My Website 138 | 139 | 140 |
141 | ${{ $$child: children }} 142 |
147 | 148 |
149 |
150 | 151 | ` 152 | ); 153 | } 154 | ``` 155 | 156 | [typed-html]: https://github.com/nicojs/typed-html 157 | [triple-slash directive]: https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html 158 | -------------------------------------------------------------------------------- /docs/docs/howto/xternal.md: -------------------------------------------------------------------------------- 1 | # Augmenting external JSX libraries 2 | 3 | typed-htmx is extremely minimal and requires the user to manually augment external JSX libraries that provide their own types. 4 | 5 | ## Common guidance 6 | 7 | - Create a `types.d.ts` (any name is fine, as long as it ends in `.d.ts`) at the top of your src/ folder, 8 | or anywhere within the configured `include` of your tsconfig.json 9 | - Write a JSX element, e.g. `
`, and inspect its type 10 | - If you see React-related types, you are good to go 11 | - If not, try to discover the common namespace under which all HTML attributes go. 12 | 13 | Let's use [Hono](https://hono.dev/top) as an example. 14 | 15 | ```tsx twoslash 16 | // @jsxImportSource: hono/jsx 17 | // In tsconfig.json, jsxImportSource = "hono/jsx" 18 | 19 | // The type we are augmenting in this case is `Hono.HTMLAttributes`. 20 | // hx-boost is not recognized as a proper attribute yet. 21 |
22 | //^? 23 | ``` 24 | 25 | With this knowledge, we can now augment the type of `Hono.HTMLAttributes` assuming it is an interface: 26 | 27 | ```tsx twoslash 28 | // @errors: 2322 29 | // @jsxImportSource: hono/jsx 30 | /// 31 | 32 | declare global { 33 | namespace Hono { 34 | interface HTMLAttributes extends HtmxAttributes {} 35 | } 36 | } 37 | 38 |
41 | ``` 42 | 43 | ## Hono 44 | 45 | ```ts twoslash 46 | import 'typed-htmx'; 47 | 48 | declare global { 49 | namespace Hono { 50 | interface HTMLAttributes extends HtmxAttributes {} 51 | } 52 | } 53 | ``` 54 | 55 | ## Astro 56 | 57 | ```ts twoslash 58 | import 'typed-htmx'; 59 | 60 | declare global { 61 | namespace astroHTML.JSX { 62 | interface IntrinsicAttributes extends HtmxAttributes {} 63 | } 64 | } 65 | ``` 66 | -------------------------------------------------------------------------------- /docs/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const lightCodeTheme = require("prism-react-renderer/themes/github"); 5 | const darkCodeTheme = require("prism-react-renderer/themes/dracula"); 6 | 7 | /** @type {import('@docusaurus/types').Config} */ 8 | const config = { 9 | title: "typed-htmx", 10 | tagline: "JSX definitions for htmx", 11 | favicon: "img/favicon.ico", 12 | 13 | url: "https://desdaemon.github.io", 14 | baseUrl: "/typed-htmx/", 15 | 16 | // GitHub pages deployment config. 17 | // If you aren't using GitHub pages, you don't need these. 18 | organizationName: "Desdaemon", // Usually your GitHub org/user name. 19 | projectName: "typed-htmx", // Usually your repo name. 20 | 21 | markdown: { 22 | format: 'md' 23 | }, 24 | onBrokenLinks: "warn", 25 | onBrokenMarkdownLinks: "warn", 26 | 27 | i18n: { 28 | defaultLocale: "en", 29 | locales: ["en"], 30 | }, 31 | 32 | plugins: [ 33 | [ 34 | "docusaurus-plugin-typedoc", 35 | /** @type {Partial} */ 36 | ({ 37 | entryPoints: ["../src/index.ts", "../src/jsx.d.ts"], 38 | tsconfig: "../tsconfig.json", 39 | hidePageTitle: true, 40 | readme: 'none', 41 | watch: process.env.npm_lifecycle_event === "start", 42 | cleanOutputDir: true, 43 | externalPattern: ["node_modules/**/*"], 44 | plugin: ["typedoc-plugin-mdn-links"], 45 | categoryOrder: ["Core", "*", "Extensions"], 46 | }), 47 | ], 48 | ], 49 | 50 | presets: [ 51 | [ 52 | "classic", 53 | /** @type {import('@docusaurus/preset-classic').Options} */ 54 | ({ 55 | docs: { 56 | sidebarPath: require.resolve("./sidebars.js"), 57 | }, 58 | theme: { 59 | customCss: require.resolve("./src/css/custom.css"), 60 | }, 61 | 62 | }), 63 | ], 64 | [ 65 | "docusaurus-preset-shiki-twoslash", 66 | /** @type {Partial} */ 67 | ({ 68 | themes: ["min-light", "nord"], 69 | defaultOptions: { 70 | noErrors: false, 71 | }, 72 | defaultCompilerOptions: { 73 | jsx: 4, // react-jsx 74 | jsxImportSource: 'typed-htmx/typed-html', 75 | target: 99, // esnext, 76 | strict: true, 77 | checkJs: true, 78 | noImplicitAny: false, 79 | module: 199, // nodenext, 80 | moduleResolution: 99, // nodenext 81 | }, 82 | includeJSDocInHover: true, 83 | alwayRaiseForTwoslashExceptions: true, 84 | disableImplicitReactImport: true, 85 | }), 86 | ], 87 | ], 88 | 89 | themeConfig: 90 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 91 | ({ 92 | // Replace with your project's social card 93 | image: "img/docusaurus-social-card.jpg", 94 | navbar: { 95 | title: "typed-htmx", 96 | logo: { 97 | alt: "My Site Logo", 98 | src: "img/logo.svg", 99 | }, 100 | items: [ 101 | { 102 | type: "docSidebar", 103 | sidebarId: "docsSidebar", 104 | position: "left", 105 | label: "Usage", 106 | }, 107 | { 108 | type: "docSidebar", 109 | sidebarId: "referenceSidebar", 110 | position: "left", 111 | label: "Reference", 112 | }, 113 | { 114 | href: "https://github.com/Desdaemon/typed-htmx", 115 | label: "GitHub", 116 | position: "right", 117 | }, 118 | ], 119 | }, 120 | footer: { 121 | style: "dark", 122 | links: [ 123 | { 124 | title: "Docs", 125 | items: [ 126 | { 127 | label: "Usage", 128 | to: "/docs/howto/intro", 129 | }, 130 | { 131 | label: "Reference", 132 | to: "/docs/api", 133 | }, 134 | ], 135 | }, 136 | ], 137 | }, 138 | prism: { 139 | theme: lightCodeTheme, 140 | darkTheme: darkCodeTheme, 141 | }, 142 | }), 143 | }; 144 | 145 | module.exports = config; 146 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "3.3.2", 18 | "@docusaurus/plugin-content-docs": "^3.3.2", 19 | "@docusaurus/preset-classic": "3.3.2", 20 | "@mdx-js/react": "^3.0.1", 21 | "clsx": "^1.2.1", 22 | "docusaurus-plugin-typedoc": "next", 23 | "docusaurus-preset-shiki-twoslash": "^1.1.41", 24 | "hono": "^3.12.12", 25 | "object-assign": "^4.1.1", 26 | "prism-react-renderer": "^1.3.5", 27 | "react": "^18.3.1", 28 | "react-dom": "^18.3.1", 29 | "typed-htmx": "link:..", 30 | "typedoc": "^0.25.13", 31 | "typedoc-plugin-markdown": "^4.0.1", 32 | "typedoc-plugin-mdn-links": "^3.1.24", 33 | "typescript": "^5.4.5" 34 | }, 35 | "devDependencies": { 36 | "@docusaurus/module-type-aliases": "3.3.2", 37 | "@docusaurus/types": "^3.3.2" 38 | }, 39 | "browserslist": { 40 | "production": [ 41 | ">0.5%", 42 | "not dead", 43 | "not op_mini all" 44 | ], 45 | "development": [ 46 | "last 1 chrome version", 47 | "last 1 firefox version", 48 | "last 1 safari version" 49 | ] 50 | }, 51 | "engines": { 52 | "node": ">=16.14" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | referenceSidebar: [{ type: "autogenerated", dirName: "api" }], 18 | docsSidebar: [{ type: "autogenerated", dirName: "howto" }], 19 | 20 | // But you can create a sidebar manually 21 | /* 22 | tutorialSidebar: [ 23 | 'intro', 24 | 'hello', 25 | { 26 | type: 'category', 27 | label: 'Tutorial', 28 | items: ['tutorial-basics/create-a-document'], 29 | }, 30 | ], 31 | */ 32 | }; 33 | 34 | module.exports = sidebars; 35 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // import clsx from 'clsx'; 3 | import styles from "./styles.module.css"; 4 | 5 | /* 6 | const FeatureList = [ 7 | { 8 | title: 'Easy to Use', 9 | Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, 10 | description: ( 11 | <> 12 | Docusaurus was designed from the ground up to be easily installed and 13 | used to get your website up and running quickly. 14 | 15 | ), 16 | }, 17 | { 18 | title: 'Focus on What Matters', 19 | Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, 20 | description: ( 21 | <> 22 | Docusaurus lets you focus on your docs, and we'll do the chores. Go 23 | ahead and move your docs into the docs directory. 24 | 25 | ), 26 | }, 27 | { 28 | title: 'Powered by React', 29 | Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, 30 | description: ( 31 | <> 32 | Extend or customize your website layout by reusing React. Docusaurus can 33 | be extended while reusing the same header and footer. 34 | 35 | ), 36 | }, 37 | ]; 38 | 39 | function Feature({Svg, title, description}) { 40 | return ( 41 |
42 |
43 | 44 |
45 |
46 |

{title}

47 |

{description}

48 |
49 |
50 | ); 51 | } 52 | */ 53 | 54 | export default function HomepageFeatures() { 55 | return ( 56 |
57 |
58 |
59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme="dark"] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | 32 | [data-theme="light"] .shiki.nord { 33 | display: none; 34 | } 35 | 36 | [data-theme="dark"] .shiki.min-light { 37 | display: none; 38 | } 39 | -------------------------------------------------------------------------------- /docs/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import clsx from "clsx"; 3 | import Link from "@docusaurus/Link"; 4 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 5 | import Layout from "@theme/Layout"; 6 | // import HomepageFeatures from '@site/src/components/HomepageFeatures'; 7 | 8 | import styles from "./index.module.css"; 9 | 10 | function HomepageHeader() { 11 | const { siteConfig } = useDocusaurusContext(); 12 | return ( 13 |
14 |
15 |

{siteConfig.title}

16 |

{siteConfig.tagline}

17 |
18 | 19 | Read the Docs 20 | 21 |
22 |
23 |
24 | ); 25 | } 26 | 27 | export default function Home() { 28 | const { siteConfig } = useDocusaurusContext(); 29 | return ( 30 | 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desdaemon/typed-htmx/aa7f1336d5ee1a915c98329aba6b0e2debed132a/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/demo.gif: -------------------------------------------------------------------------------- 1 | ../../static/demo.gif -------------------------------------------------------------------------------- /docs/static/img/docusaurus-social-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desdaemon/typed-htmx/aa7f1336d5ee1a915c98329aba6b0e2debed132a/docs/static/img/docusaurus-social-card.jpg -------------------------------------------------------------------------------- /docs/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desdaemon/typed-htmx/aa7f1336d5ee1a915c98329aba6b0e2debed132a/docs/static/img/docusaurus.png -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desdaemon/typed-htmx/aa7f1336d5ee1a915c98329aba6b0e2debed132a/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/undraw_docusaurus_mountain.svg: -------------------------------------------------------------------------------- 1 | 2 | Easy to Use 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /docs/static/img/undraw_docusaurus_react.svg: -------------------------------------------------------------------------------- 1 | 2 | Powered by React 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /docs/static/img/undraw_docusaurus_tree.svg: -------------------------------------------------------------------------------- 1 | 2 | Focus on What Matters 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://typedoc.org/schema.json", 3 | "categoryOrder": ["core", "*"], 4 | "sourceLinkExternal": true, 5 | "excludeExternals": true, 6 | "externalPattern": [ 7 | "**/node_modules/**/*" 8 | ], 9 | "readme": "bogus", 10 | "searchCategoryBoosts": { 11 | "Core": 2 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | A demo of library-agnostic type augmentation by typed-htmx. 4 | 5 | Try swapping out different `tsconfig.json`s to see the types of different libraries. 6 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "author": "", 5 | "main": "index.jsx", 6 | "description": "", 7 | "keywords": [], 8 | "license": "ISC", 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "dependencies": { 13 | "typed-htmx": "link:.." 14 | }, 15 | "devDependencies": { 16 | "hono": "^3.11.12" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | typed-htmx: 12 | specifier: link:.. 13 | version: link:.. 14 | devDependencies: 15 | hono: 16 | specifier: ^3.11.12 17 | version: 3.11.12 18 | 19 | packages: 20 | 21 | hono@3.11.12: 22 | resolution: {integrity: sha512-TrxH75bc0m2UbvrhaXkoo32A9OhkJtvICAYgYWtxqLDOxBjRqSikyp4K7HTbnWkPeg9Z+2Q3nv0dN4o8kL6yLg==} 23 | engines: {node: '>=16.0.0'} 24 | 25 | snapshots: 26 | 27 | hono@3.11.12: {} 28 | -------------------------------------------------------------------------------- /example/src/index.jsx: -------------------------------------------------------------------------------- 1 | export function MyComponent() { 2 | return ( 3 | "" + 4 | ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 17 | 18 |
19 | 20 | 21 | ) 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /example/src/types.d.ts: -------------------------------------------------------------------------------- 1 | // typed-htmx declares mostly ambient types so this is all you need. 2 | import "typed-htmx"; 3 | 4 | // A demo of how to augment foreign types with htmx attributes. 5 | // In this case, Hono sources its types from its own namespace, so we do the same 6 | // and directly extend its namespace. 7 | declare global { 8 | namespace Hono { 9 | interface HTMLAttributes extends HtmxAttributes {} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /example/tsconfig.hono.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | // This is (usually) the only setting to change when swapping renderers. 5 | "jsxImportSource": "hono/jsx" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 4 | // "jsx": "react", 5 | "jsx": "react-jsx" /* Specify what JSX code is generated. */, 6 | // If your frontend framework prescribes a JSX source, change it here. 7 | // In this example, we will use typed-htmx's own text renderer based on typed-html. 8 | "jsxImportSource": "typed-htmx/typed-html", 9 | "module": "node16", 10 | "moduleResolution": "node16" /* Specify how TypeScript looks up a file from a given module specifier. */, 11 | "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */, 12 | "checkJs": true /* Enable error reporting in type-checked JavaScript files. */, 13 | "noEmit": true, 14 | "rootDir": ".", 15 | "outDir": "./dist", 16 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, 17 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 18 | "strict": true /* Enable all strict type-checking options. */, 19 | "skipDefaultLibCheck": true /* Skip type checking .d.ts files that are included with TypeScript. */, 20 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 21 | }, 22 | "include": ["src/**/*"] 23 | } 24 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: typed-htmx 2 | repo_url: https://github.com/Desdaemon/typed-htmx 3 | edit_uri: edit/main/docs/ 4 | markdown_extensions: 5 | - pymdownx.highlight: 6 | anchor_linenums: true 7 | line_spans: __span 8 | pygments_lang_class: true 9 | - pymdownx.inlinehilite 10 | - pymdownx.snippets 11 | - pymdownx.superfences 12 | - footnotes 13 | - toc: 14 | permalink: true 15 | toc_depth: 3 16 | theme: 17 | name: material 18 | features: 19 | - navigation.instant 20 | - navigation.tracking 21 | - navigation.tabs 22 | - navigation.sections 23 | - toc.follow 24 | - navigation.top 25 | palette: 26 | - scheme: default 27 | media: "(prefers-color-scheme: light)" 28 | primary: teal 29 | toggle: 30 | icon: material/brightness-7 31 | name: Switch to dark mode 32 | - scheme: slate 33 | media: "(prefers-color-scheme: dark)" 34 | primary: teal 35 | toggle: 36 | icon: material/brightness-4 37 | name: Switch to light mode 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typed-htmx", 3 | "version": "0.3.1", 4 | "description": "Definitions for htmx attributes in JSX", 5 | "scripts": { 6 | "dist": "bash scripts/dist.sh", 7 | "lint": "sg scan -U && prettier --write src .", 8 | "doc": "typedoc && poetry run mkdocs" 9 | }, 10 | "keywords": [ 11 | "typescript", 12 | "typesafe", 13 | "jsx", 14 | "HTML", 15 | "template", 16 | "AJAX" 17 | ], 18 | "homepage": "https://desdaemon.github.io/typed-htmx/", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/Desdaemon/typed-htmx.git" 22 | }, 23 | "author": "Viet Dinh", 24 | "license": "ISC", 25 | "engines": { 26 | "node": ">=12" 27 | }, 28 | "files": [ 29 | "package.json", 30 | "pnpm-lock.yaml", 31 | "LICENSE", 32 | "dist", 33 | "src/jsx.d.ts" 34 | ], 35 | "module": "./dist/esm/index.js", 36 | "main": "./dist/index.js", 37 | "types": "./dist/index.d.ts", 38 | "exports": { 39 | ".": { 40 | "import": "./dist/esm/index.js", 41 | "require": "./dist/index.js", 42 | "types": "./dist/index.d.ts" 43 | }, 44 | "./typed-html/jsx-runtime": { 45 | "import": "./dist/esm/typed-html/jsx-runtime.js", 46 | "require": "./dist/typed-html/jsx-runtime.js", 47 | "types": "./dist/typed-html/jsx-runtime.d.ts" 48 | }, 49 | "./typed-html/jsx-dev-runtime": { 50 | "import": "./dist/esm/typed-html/jsx-dev-runtime.js", 51 | "require": "./dist/typed-html/jsx-dev-runtime.js", 52 | "types": "./dist/typed-html/jsx-dev-runtime.d.ts" 53 | } 54 | }, 55 | "dependencies": { 56 | "typed-html": "^3.0.1" 57 | }, 58 | "devDependencies": { 59 | "cross-env": "^7.0.3", 60 | "prettier": "^3.2.5" 61 | } 62 | } -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | typed-html: 12 | specifier: ^3.0.1 13 | version: 3.0.1 14 | devDependencies: 15 | cross-env: 16 | specifier: ^7.0.3 17 | version: 7.0.3 18 | prettier: 19 | specifier: ^3.2.5 20 | version: 3.2.5 21 | 22 | packages: 23 | 24 | cross-env@7.0.3: 25 | resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} 26 | engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} 27 | hasBin: true 28 | 29 | cross-spawn@7.0.3: 30 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 31 | engines: {node: '>= 8'} 32 | 33 | isexe@2.0.0: 34 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 35 | 36 | path-key@3.1.1: 37 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 38 | engines: {node: '>=8'} 39 | 40 | prettier@3.2.5: 41 | resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} 42 | engines: {node: '>=14'} 43 | hasBin: true 44 | 45 | shebang-command@2.0.0: 46 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 47 | engines: {node: '>=8'} 48 | 49 | shebang-regex@3.0.0: 50 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 51 | engines: {node: '>=8'} 52 | 53 | typed-html@3.0.1: 54 | resolution: {integrity: sha512-JKCM9zTfPDuPqQqdGZBWSEiItShliKkBFg5c6yOR8zth43v763XkAzTWaOlVqc0Y6p9ee8AaAbipGfUnCsYZUA==} 55 | engines: {node: '>=12'} 56 | 57 | which@2.0.2: 58 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 59 | engines: {node: '>= 8'} 60 | hasBin: true 61 | 62 | snapshots: 63 | 64 | cross-env@7.0.3: 65 | dependencies: 66 | cross-spawn: 7.0.3 67 | 68 | cross-spawn@7.0.3: 69 | dependencies: 70 | path-key: 3.1.1 71 | shebang-command: 2.0.0 72 | which: 2.0.2 73 | 74 | isexe@2.0.0: {} 75 | 76 | path-key@3.1.1: {} 77 | 78 | prettier@3.2.5: {} 79 | 80 | shebang-command@2.0.0: 81 | dependencies: 82 | shebang-regex: 3.0.0 83 | 84 | shebang-regex@3.0.0: {} 85 | 86 | typed-html@3.0.1: {} 87 | 88 | which@2.0.2: 89 | dependencies: 90 | isexe: 2.0.0 91 | -------------------------------------------------------------------------------- /rules/no-extensionless-relative-import.yml: -------------------------------------------------------------------------------- 1 | id: no-extensionless-relative-import 2 | message: Relative imports must end in .js 3 | severity: error 4 | language: ts 5 | rule: 6 | pattern: "$IMPORT" 7 | regex: "/([^.]+)[^/]$" 8 | kind: string_fragment 9 | any: 10 | - inside: 11 | stopBy: end 12 | kind: import_statement 13 | - inside: 14 | stopBy: end 15 | kind: export_statement 16 | - inside: 17 | stopBy: end 18 | kind: call_expression 19 | has: 20 | field: function 21 | regex: "^import$" 22 | transform: 23 | OUTPUT: 24 | replace: 25 | replace: ".*" 26 | by: "$0.js" 27 | source: $IMPORT 28 | fix: "$OUTPUT" 29 | -------------------------------------------------------------------------------- /scripts/dist.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | rm -rf ./dist 5 | tsc -b 6 | tsc -p tsconfig.esm.json 7 | echo '{"type":"module"}' > ./dist/esm/package.json 8 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | npm whoami 5 | npm version ${1:-patch} 6 | VERSION=$(cat package.json | jq -r '.version') 7 | git tag -f v$VERSION 8 | git push -u origin v$VERSION --tags -f 9 | gh release create v$VERSION -t v$VERSION --generate-notes --draft --verify-tag 10 | npm publish --dry-run 11 | npm publish -------------------------------------------------------------------------------- /sgconfig.yml: -------------------------------------------------------------------------------- 1 | ruleDirs: [rules] 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | /** 4 | * Configuration for the JSX runtime. 5 | */ 6 | export const jsxConfig: JsxConfig = { 7 | jsonAttributes: new Set(["hx-vals", "hx-headers", "data-hx-vals", "data-hx-headers"]), 8 | sanitize: false, 9 | trusted: false, 10 | }; 11 | 12 | export interface JsxConfig { 13 | /** 14 | * When these attributes' values are set to object literals, they will be stringified to JSON. 15 | */ 16 | jsonAttributes: Set; 17 | /** 18 | * The sanitizer to be used by the runtime. 19 | * Accepts a function of the signature `(raw: string, originalType: string) => string`. 20 | * @note {@link JsxConfig.trusted} must be false for elements to be sanitized. 21 | * @see {@link Sanitizer} 22 | */ 23 | sanitize: Sanitizer; 24 | /** 25 | * If false, value interpolations inside of JSX will be sanitized. 26 | * @note Sanitization will change the return type of JSX functions to an object that overrides `toString`. 27 | * In most cases it will function as expected, but you might sometimes need to manually coerce the JSX tree to a string. 28 | */ 29 | trusted: boolean; 30 | } 31 | 32 | export type Sanitizer = false | ((raw: string, originalType: string) => string); 33 | 34 | export type InterpValue = 35 | | { $$child: unknown } 36 | | { $$children: unknown[] } 37 | | { $$spread: unknown } 38 | | Record; 39 | 40 | export type HtmlTemplator = (raw: TemplateStringsArray, ...values: InterpValue[]) => Output; 41 | 42 | const attrPattern = /[<>&"']/g; 43 | const attrPatternWithoutDQ = /[<>&']/g; 44 | const attrReplacements: Record = { 45 | "<": "<", 46 | ">": ">", 47 | "&": "&", 48 | '"': """, 49 | "'": "'", 50 | }; 51 | 52 | type Renderable = 0 | {}; 53 | function isRenderable(value: unknown): value is Renderable { 54 | return value === 0 || !!value; 55 | } 56 | function attrSanitizer(raw: Renderable): string { 57 | return String(raw).replace(attrPattern, (sub) => attrReplacements[sub] || sub); 58 | } 59 | function attrSanitizerWithoutDQ(raw: Renderable): string { 60 | return String(raw).replace(attrPatternWithoutDQ, (sub) => attrReplacements[sub] || sub); 61 | } 62 | function htmlSanitizer(raw: Renderable): string { 63 | const out = String(raw); 64 | if (jsxConfig.trusted) return out; 65 | return jsxConfig.sanitize ? jsxConfig.sanitize(out, typeof raw) : out; 66 | } 67 | 68 | function isObject(value: unknown): value is {} { 69 | return typeof value === "object" && value !== null; 70 | } 71 | 72 | function htmlTransformChildren(value: InterpValue): string { 73 | if ("$$child" in value) return isRenderable(value.$$child) ? htmlSanitizer(value.$$child) : ""; 74 | if ("$$children" in value) { 75 | const out: string[] = []; 76 | for (const child of value.$$children as unknown[]) { 77 | if (isRenderable(child)) out.push(htmlSanitizer(child)); 78 | } 79 | return out.join(" "); 80 | } 81 | 82 | let obj: Record; 83 | if ("$$spread" in value && isObject(value.$$spread)) obj = value.$$spread; 84 | else if (isObject(value)) obj = value; 85 | else return ""; 86 | const out: string[] = []; 87 | for (const key of Object.keys(obj)) { 88 | const attr = obj[key]; 89 | // @ts-expect-error !Renderable is not null | undefined 90 | if (!isRenderable(attr) && attr !== "") continue; 91 | if (jsxConfig.jsonAttributes.has(key)) { 92 | out.push(`${key}='${attrSanitizerWithoutDQ(JSON.stringify(attr))}'`); 93 | } else { 94 | out.push(`${key}="${attrSanitizer(attr)}"`); 95 | } 96 | } 97 | return out.join(" "); 98 | } 99 | 100 | /** 101 | * A [tagged template](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) 102 | * that interprets different kinds of {@link InterpValue values} into escaped HTML. 103 | * 104 | * ```ts twoslash 105 | * import { html } from 'typed-htmx'; 106 | * function assertEqual(left: any, right: any) {} 107 | * // ---cut--- 108 | * const template = html` 109 | *
110 | * `; 111 | * assertEqual(template, `
`); 112 | * ``` 113 | */ 114 | export const html: HtmlTemplator = (raw, ...values) => { 115 | const values_ = values.map(htmlTransformChildren); 116 | return String.raw(raw, ...values_); 117 | }; 118 | -------------------------------------------------------------------------------- /src/jsx.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides type definitions in JSX for htmx attributes. 3 | * @module 4 | */ 5 | 6 | /** 7 | * Either `"true"` or `"false"`. 8 | */ 9 | type BoolStr = "true" | "false"; 10 | type AnyStr = string & {}; 11 | type HxSwap = 12 | | "innerHTML" 13 | | "outerHTML" 14 | | "beforebegin" 15 | | "afterbegin" 16 | | "beforeend" 17 | | "afterend" 18 | | "delete" 19 | | "none" 20 | | "morph" 21 | | "morphdom" 22 | | "multi:"; 23 | 24 | /** 25 | * Either `this` which refers to the element itself, or a modifier followed by a CSS selector, e.g. `closest form`. 26 | */ 27 | type HxTarget = "this" | "closest " | "find " | "next" | "next " | "previous" | "previous "; 28 | 29 | /** 30 | * A CSS selector, followed by one of these sync strategies, e.g. `form:abort`. 31 | */ 32 | type HxSync = ":drop" | ":abort" | ":replace" | ":queue" | ":queue first" | ":queue last" | ":queue all"; 33 | 34 | /** 35 | * Any of the standard DOM events, or htmx-specific events. 36 | */ 37 | type HxTriggers = keyof GlobalEventHandlersEventMap | HtmxUtils.HtmxEvents; 38 | 39 | /** @ignore */ 40 | declare namespace HtmxUtils { 41 | type HxOnMap = 42 | { [K in keyof GlobalEventHandlersEventMap as `hx-on-${K}`]?: string; } & 43 | { [K in HxOnHtmxEvents as `hx-on--${K}`]?: string; } 44 | 45 | type HxOnHtmxEvents = 46 | | JsxHtmxEvents 47 | | keyof { [K in keyof HxSubevents as `${K}-${HxSubevents[K]}`]: never } 48 | 49 | type JsxHtmxEvents = 50 | | "abort" 51 | | "after-on-load" 52 | | "after-process-node" 53 | | "after-request" 54 | | "after-settle" 55 | | "after-swap" 56 | | "before-cleanup-element" 57 | | "before-on-load" 58 | | "before-process-node" 59 | | "before-request" 60 | | "before-swap" 61 | | "before-send" 62 | | "config-request" 63 | | "confirm" 64 | | "history-cache-error" 65 | | "history-cache-miss" 66 | | "history-cache-miss-error" 67 | | "history-cache-miss-load" 68 | | "history-restore" 69 | | "before-history-save" 70 | | "load" 71 | | "no-sse-source-error" 72 | | "on-load-error" 73 | | "oob-after-swap" 74 | | "oob-before-swap" 75 | | "oob-error-no-target" 76 | | "prompt" 77 | | "pushed-into-history" 78 | | "response-error" 79 | | "send-error" 80 | | "sse-error" 81 | | "sse-open" 82 | | "swap-error" 83 | | "target-error" 84 | | "timeout" 85 | 86 | type HxSubevents = { 87 | validation: 'validate' | 'failed' | 'halted'; 88 | xhr: 'abort' | 'loadend' | 'loadstart' | 'progress'; 89 | } 90 | 91 | type KebabToCamel = 92 | T extends `${infer Head}-${infer Rest}` 93 | ? `${Head}${KebabToCamel>}` 94 | : T 95 | 96 | type HtmxEvents = 97 | | `htmx:${KebabToCamel}` 98 | | keyof { [K in keyof HxSubevents as `htmx:${K}:${HxSubevents[K]}`]: never } 99 | } 100 | 101 | /** 102 | * An event followed by one of these modifiers, e.g. `click once`. 103 | */ 104 | type HxTriggerModifier = 105 | | " once" 106 | | " changed" 107 | | " delay:" 108 | | " throttle:" 109 | | " from:" 110 | | " target:" 111 | | " consume" 112 | | " queue:first" 113 | | " queue:last" 114 | | " queue:all" 115 | | " queue:none"; 116 | 117 | /** 118 | * An extensible directory of htmx extensions. 119 | * 120 | * ### Declaring a new extension 121 | * 122 | * ```tsx twoslash 123 | * // in foo.d.ts: 124 | * 125 | * declare global { 126 | * namespace JSX { 127 | * interface HtmxExtensions { 128 | * myExtension: "my-extension"; 129 | * } 130 | * interface HtmlTag { 131 | * /** Describe your attribute *\/ 132 | * ["my-extension-attr"]?: "true" | "false"; 133 | * // Add any other attributes your extension uses here 134 | * } 135 | * } 136 | * } 137 | * 138 | *
139 | * Hello 140 | * // ^? 141 | *
142 | * ``` 143 | */ 144 | interface HtmxBuiltinExtensions { 145 | /** 146 | * Includes the commonly-used `X-Requested-With` header that identifies ajax requests in many backend frameworks. 147 | * 148 | * CDN: https://unpkg.com/htmx.org/dist/ext/ajax-header.js 149 | * @see https://htmx.org/extensions/ajax-header/ 150 | */ 151 | ajaxHeaders: "ajax-headers"; 152 | /** 153 | * Uses the Alpine.js morph plugin to swap content. 154 | * 155 | * CDN: https://unpkg.com/htmx.org/dist/ext/alpine-morph.js 156 | * @see https://htmx.org/extensions/alpine-morph/ 157 | */ 158 | alpineMorph: "alpine-morph"; 159 | /** 160 | * Ingest [server-sent events]. 161 | * 162 | * CDN: https://unpkg.com/htmx.org/dist/ext/sse.js 163 | * @see https://htmx.org/extensions/server-sent-events/ 164 | * 165 | * [server-sent Events]: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events 166 | */ 167 | serverSentEvents: "sse"; 168 | /** 169 | * WebSockets support. 170 | * 171 | * CDN: https://unpkg.com/htmx.org/dist/ext/ws.js 172 | * @see https://htmx.org/extensions/web-sockets/ 173 | */ 174 | ws: "ws"; 175 | /** 176 | * Class utilities. 177 | * 178 | * CDN: https://unpkg.com/htmx.org/dist/ext/class-tools.js 179 | * @see https://htmx.org/extensions/class-tools/ 180 | */ 181 | classTools: "class-tools"; 182 | /** 183 | * Transforms JSON/XML responses into HTML. 184 | * 185 | * CDN: https://unpkg.com/htmx.org/dist/ext/client-side-templates.js 186 | * @see https://htmx.org/extensions/client-side-templates/ 187 | */ 188 | clientSideTemplates: "client-side-templates"; 189 | /** 190 | * Tool for debugging htmx requests. 191 | * 192 | * CDN: https://unpkg.com/htmx.org/dist/ext/debug.js 193 | * @see https://htmx.org/extensions/debug/ 194 | */ 195 | debug: "debug"; 196 | /** 197 | * Disable elements during requests. 198 | * 199 | * CDN: https://unpkg.com/htmx.org/dist/ext/disable-element.js 200 | * @see https://htmx.org/extensions/disable-element/ 201 | * @deprecated 1.9.6: Included into htmx core as `hx-disabled-elt`. 202 | */ 203 | disableElement: "disable-element"; 204 | /** 205 | * Includes the JSON of the event that triggered a request 206 | * to the `Triggering-Event` header. 207 | * 208 | * CDN: https://unpkg.com/htmx.org/dist/ext/event-header.js 209 | * @see https://htmx.org/extensions/event-header/ 210 | */ 211 | eventHeader: "event-header"; 212 | /** 213 | * Support for adding tags to ``. 214 | * 215 | * CDN: https://unpkg.com/htmx.org/dist/ext/head-support.js 216 | * @see https://htmx.org/extensions/head-support/ 217 | */ 218 | headSupport: "head-support"; 219 | /** 220 | * Include additional data in requests. 221 | * 222 | * CDN: https://unpkg.com/htmx.org/dist/ext/include-vals.js 223 | */ 224 | includeVals: "include-vals"; 225 | /** 226 | * Support for [Idiomorph](https://github.com/bigskysoftware/idiomorph), an alternative swapping mechanism for htmx. 227 | * 228 | * CDN: https://unpkg.com/idiomorph/dist/idiomorph-ext.min.js 229 | * @see https://github.com/bigskysoftware/idiomorph#htmx 230 | */ 231 | idiomorph: "morph"; 232 | /** 233 | * Use JSON encoding in the body of requests, rather than the default `x-www-form-urlencoded`. 234 | * 235 | * CDN: https://unpkg.com/htmx.org/dist/ext/json-enc.js 236 | * @see https://htmx.org/extensions/json-enc/ 237 | */ 238 | jsonEncode: "json-enc"; 239 | /** 240 | * Support for inflight loading states. 241 | * 242 | * CDN: https://unpkg.com/htmx.org/dist/ext/loading-states.js 243 | * @see https://htmx.org/extensions/loading-states/ 244 | */ 245 | loadingStates: "loading-states"; 246 | /** 247 | * Support for [morphdom](https://github.com/patrick-steele-idem/morphdom), 248 | * an alternative swapping mechanism for htmx. 249 | * 250 | * CDN: https://unpkg.com/htmx.org/dist/ext/morphdom-swap.js 251 | * @see https://htmx.org/extensions/morphdom-swap/ 252 | */ 253 | morphdomSwap: "morphdom-swap"; 254 | /** 255 | * Use `X-HTTP-Method-Override` for non-GET and -POST requests. 256 | * Useful for bypassing firewalls or proxies. 257 | * 258 | * CDN: https://unpkg.com/htmx.org/dist/ext/method-override.js 259 | * @see https://htmx.org/extensions/method-override/ 260 | */ 261 | methodOverride: "method-override"; 262 | /** 263 | * Swap multiple elements marked with IDs, each optionally followed by a swap style. 264 | * 265 | * CDN: https://unpkg.com/htmx.org/dist/ext/multi-swap.js 266 | * @see https://htmx.org/extensions/multi-swap/ 267 | */ 268 | multiSwap: "multi-swap"; 269 | /** 270 | * Express dependencies between requests. 271 | * 272 | * CDN: https://unpkg.com/htmx.org/dist/ext/path-deps.js 273 | * @see https://htmx.org/extensions/path-deps/ 274 | */ 275 | pathDeps: "path-deps"; 276 | /** 277 | * Preload HTML fragments. 278 | * 279 | * CDN: https://unpkg.com/htmx.org/dist/ext/preload.js 280 | * @see https://htmx.org/extensions/preload/ 281 | */ 282 | preload: "preload"; 283 | /** 284 | * Remove this element after a set duration. 285 | * 286 | * CDN: https://unpkg.com/htmx.org/dist/ext/remove-me.js 287 | * @see https://htmx.org/extensions/remove-me/ 288 | */ 289 | removeMe: "remove-me"; 290 | /** 291 | * Specify different target elements for different HTTP response codes. 292 | * 293 | * CDN: https://unpkg.com/htmx.org/dist/ext/response-targets.js 294 | * @see https://htmx.org/extensions/response-targets/ 295 | */ 296 | responseTargets: "response-targets"; 297 | /** 298 | * Triggers an event `restored` when a back button event is detected while using `hx-boost`. 299 | * 300 | * CDN: https://unpkg.com/htmx.org/dist/ext/restored.js 301 | * @see https://htmx.org/extensions/restored/ 302 | */ 303 | restored: "restored"; 304 | /** 305 | * Use specific parameters as path variables. 306 | * 307 | * CDN: https://unpkg.com/htmx.org/dist/ext/path-params.js 308 | * @see https://htmx.org/extensions/path-params/ 309 | */ 310 | pathParams: "path-params"; 311 | } 312 | 313 | /** 314 | * Definitions for htmx attributes up to 1.9.10. 315 | * 316 | * ###### Path variables 317 | * `hx-get`, `hx-post` and other request attributes can include path variables by 318 | * using the {@linkcode HtmxBuiltinExtensions.pathParams path-params} extension. 319 | * Once used as a path variable, it will not be included in the request body. 320 | * ```jsx twoslash 321 | * 322 | * // Only 'foo' will be included in the request body 323 | * ``` 324 | */ 325 | interface HtmxAttributes extends HtmxUtils.HxOnMap { 326 | /** @ignore For React compatibility only. */ 327 | children?: {} | null; 328 | /** @ignore For React compatibility only. */ 329 | key?: {}; 330 | /** 331 | * Issues a `GET` to the specified URL. 332 | * @see https://htmx.org/attributes/hx-get/ 333 | * @category Core 334 | */ 335 | ["hx-get"]?: string; 336 | /** 337 | * Issues a `POST` to the specified URL. 338 | * @see https://htmx.org/attributes/hx-post/ 339 | * @category Core 340 | */ 341 | ["hx-post"]?: string; 342 | /** 343 | * Issues a `PUT` to the specified URL. 344 | * @see https://htmx.org/attributes/hx-put/ 345 | */ 346 | ["hx-put"]?: string; 347 | /** 348 | * Issues a `DELETE` to the specified URL. 349 | * @see https://htmx.org/attributes/hx-delete/ 350 | */ 351 | ["hx-delete"]?: string; 352 | /** 353 | * Issues a `PATCH` to the specified URL. 354 | * @see https://htmx.org/attributes/hx-patch/ 355 | */ 356 | ["hx-patch"]?: string; 357 | /** 358 | * Add or remove [progressive enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement) 359 | * for links and forms. 360 | * 361 | * @see https://htmx.org/attributes/hx-boost/ 362 | * @category Core 363 | */ 364 | ["hx-boost"]?: BoolStr; 365 | /** 366 | * Handle any event with a script inline. 367 | * @see https://htmx.org/attributes/hx-on/ 368 | * @category Core 369 | * @remarks Event listeners on htmx-specific events need to be specified with a spread attribute, and 370 | * are otherwise not supported in vanilla JSX. 371 | * Alternatively, use the `hx-on-` all-dash syntax. (since 1.9.10) 372 | * ```jsx 373 | *
374 | * ``` 375 | * @since 1.9.3 376 | */ 377 | ["hx-on:"]?: string; 378 | /** 379 | * Alternative syntax for `hx-on:`. 380 | * 381 | * All colons in event handlers can be replaced with a dash, i.e. 382 | * `hx-on:click` becomes `hx-on-click`, `hx-on::before-request` becomes `hx-on--before-request` and so on. 383 | * @see https://htmx.org/attributes/hx-on/ 384 | * @category Core 385 | * @since 1.9.10 386 | */ 387 | ["hx-on-"]?: string; 388 | /** 389 | * Handle any event with a script inline. Each listener is specified on a separate line. 390 | * @see https://htmx.org/attributes/hx-on/ 391 | * @remarks Superseded by `hx-on:$event`, unless IE11 support is required. 392 | * @since 1.9.0 393 | */ 394 | ["hx-on"]?: HxTriggers | AnyStr; 395 | /** 396 | * Pushes the URL into the browser location bar, creating a new history entry. 397 | * @see https://htmx.org/attributes/hx-push-url/ 398 | * @category Core 399 | */ 400 | ["hx-push-url"]?: BoolStr | AnyStr; 401 | /** 402 | * CSS selector for content to swap in from a response. 403 | * @see https://htmx.org/attributes/hx-select/ 404 | * @category Core 405 | */ 406 | ["hx-select"]?: string; 407 | /** 408 | * CSS selector for content to swap in from a response, out of band (somewhere other than the target). 409 | * @see https://htmx.org/attributes/hx-select-oob/ 410 | * @category Core 411 | */ 412 | ["hx-select-oob"]?: string; 413 | /** 414 | * Controls how content is swapped in (`outerHTML`, `beforeend`, `afterend`, …). 415 | * @default htmx.config.defaultSwapStyle // 'innerHTML' 416 | * @see https://htmx.org/attributes/hx-swap/ 417 | * @see [`InsertPosition`](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML#position) which is used in `Element.insertAdjacentHTML`. 418 | * @category Core 419 | * @remarks 420 | * - `morph` is observed by the {@linkcode HtmxBuiltinExtensions.idiomorph idiomorph} extension. 421 | * Also see {@linkcode HtmxBuiltinExtensions.alpineMorph alpine-morph}. 422 | * - `morphdom` swaps are observed by the {@linkcode HtmxBuiltinExtensions.morphdomSwap morphdom-swap} extension. 423 | * - `multi:` swaps are observed by the {@linkcode HtmxBuiltinExtensions.multiSwap multi-swap} extension. 424 | * @since 1.9.6: `hx-swap` can be specified without any swap style. 425 | */ 426 | ["hx-swap"]?: true | HxSwap | AnyStr; 427 | /** 428 | * Marks content in a response to be out of band (should swap in somewhere other than the target). 429 | * @see https://htmx.org/attributes/hx-swap-oob/ 430 | */ 431 | ["hx-swap-oob"]?: "true" | HxSwap | AnyStr; 432 | /** 433 | * Specifies the target element to be swapped. 434 | * 435 | * Accepts a CSS selector, optionally preceded by a {@link HxTarget modifier}. 436 | * @see https://htmx.org/attributes/hx-target/ 437 | * @category Core 438 | */ 439 | ["hx-target"]?: HxTarget | AnyStr; 440 | /** 441 | * Specifies the target element to be swapped, when a specific error code is encountered. 442 | * 443 | * Error codes may also be specified as wildcards, e.g. `hx-target-5*` or `hx-target-5x`. 444 | * @see https://htmx.org/extensions/response-targets/ 445 | * @category Extensions 446 | */ 447 | ["hx-target-"]?: HxTarget | AnyStr; 448 | /** 449 | * Specifies a catch-all target element to be swapped when a 4xx or 5xx response is encountered. 450 | * @see https://htmx.org/extensions/response-targets/ 451 | * @category Extensions 452 | */ 453 | ["hx-target-error"]?: HxTarget | AnyStr; 454 | /** 455 | * Specifies the event that triggers the request. 456 | * 457 | * Accepts {@link HxTriggers names} of standard DOM events, or htmx-specific events. 458 | * Optionally followed by a {@link HxTriggerModifier modifier}. 459 | * @see https://htmx.org/attributes/hx-trigger/ 460 | * @category Core 461 | * @remarks 462 | * - `sse:` is observed by the {@linkcode HtmxBuiltinExtensions.serverSentEvents server-sent-events} extension. 463 | * - `path-deps` is observed by the {@linkcode HtmxBuiltinExtensions.pathDeps path-deps} extension. 464 | * - `restored` is observed by the {@linkcode HtmxBuiltinExtensions.restored restored} extension. 465 | */ 466 | ["hx-trigger"]?: "every " | HxTriggerModifier | HxTriggers | "sse:" | "path-deps" | "restored" | AnyStr; 467 | /** 468 | * Adds values to the parameters to submit with the request (JSON-formatted). 469 | * 470 | * Objects may be passed only if supported by the JSX renderer. 471 | * @see https://htmx.org/attributes/hx-params/ 472 | * @category Core 473 | */ 474 | ["hx-vals"]?: AnyStr | "javascript:" | "js:" | Record; 475 | /** 476 | * Shows a `confirm()` dialog before issuing a request. 477 | * @see https://htmx.org/attributes/hx-confirm/ 478 | */ 479 | ["hx-confirm"]?: string; 480 | /** 481 | * Disables htmx processing for the given node and any children nodes. 482 | * Useful for escaping user-generated content. 483 | * @see https://htmx.org/attributes/hx-disable/ 484 | */ 485 | ["hx-disable"]?: true; 486 | /** 487 | * Marks the element to be disabled during a request. 488 | * Multiple elements are separated by commas. 489 | * @see https://htmx.org/attributes/hx-disabled-elt/ 490 | * @since 1.9.6 491 | */ 492 | ["hx-disabled-elt"]?: "this" | "closest " | AnyStr; 493 | /** 494 | * Control and disable automatic attribute inheritance for child nodes. 495 | * @see https://htmx.org/attributes/hx-disinherit/ 496 | */ 497 | ["hx-disinherit"]?: "*" | AnyStr; 498 | /** 499 | * Changes the request encoding type. 500 | * @see https://htmx.org/attributes/hx-encoding/ 501 | */ 502 | ["hx-encoding"]?: "multipart/form-data"; 503 | /** 504 | * Extensions to apply to this element and its descendants. 505 | * @see https://htmx.org/attributes/hx-ext/ 506 | * @see {@linkcode HtmxBuiltinExtensions} for extension details, and how to declare extensions in JSX. 507 | */ 508 | ["hx-ext"]?: JSX.HtmxExtensions[keyof JSX.HtmxExtensions] | "ignore:" | AnyStr; 509 | /** 510 | * Adds to the headers that will be submitted with the request. 511 | * 512 | * Objects may be passed only if supported by the JSX renderer. 513 | * @see https://htmx.org/attributes/hx-headers/ 514 | */ 515 | ["hx-headers"]?: AnyStr | "javascript:" | "js:" | Record; 516 | /** 517 | * Prevent sensitive data being saved to the history cache. 518 | * @see https://htmx.org/attributes/hx-history/ 519 | */ 520 | ["hx-history"]?: "false"; 521 | /** 522 | * Mark the element to snapshot and restore during history navigation. 523 | * @see https://htmx.org/attributes/hx-history-elt/ 524 | */ 525 | ["hx-history-elt"]?: true; 526 | /** 527 | * Include additional data in requests. 528 | * Accepts CSS selectors. 529 | * @see https://htmx.org/attributes/hx-include/ 530 | */ 531 | ["hx-include"]?: "this" | "closest " | "find " | "next " | "previous " | AnyStr; 532 | /** 533 | * The element to put the `htmx-request` class on during the request. 534 | * Accepts CSS selectors. 535 | * @see https://htmx.org/attributes/hx-indicator/ 536 | */ 537 | ["hx-indicator"]?: "closest " | AnyStr; 538 | /** 539 | * Filters the parameters that will be submitted with a request. 540 | * @see https://htmx.org/attributes/hx-params/ 541 | */ 542 | ["hx-params"]?: "*" | "none" | "not " | AnyStr; 543 | /** 544 | * Specifies elements to keep unchanged between requests. 545 | * An ID on the element is also required, and is the only criterion for preservation. 546 | * @see https://htmx.org/attributes/hx-preserve/ 547 | * @remarks This attribute is observed by the [`head-support`] extension, 548 | * where it prevents an element from being removed from ``. 549 | * 550 | * [`head-support`]: https://htmx.org/extensions/head-support/ 551 | */ 552 | ["hx-preserve"]?: "true"; 553 | /** 554 | * Shows a `prompt()` before submitting a request. 555 | * @see https://htmx.org/attributes/hx-prompt/ 556 | */ 557 | ["hx-prompt"]?: string; 558 | /** 559 | * Replace the URL in the browser location bar. 560 | * @see https://htmx.org/attributes/hx-replace-url/ 561 | */ 562 | ["hx-replace-url"]?: BoolStr | AnyStr; 563 | /** 564 | * Configures various aspects of the request. 565 | * @see https://htmx.org/attributes/hx-request/ 566 | */ 567 | ["hx-request"]?: '"timeout":' | '"credentials":' | '"noHeaders":' | "javascript:" | "js:" | AnyStr; 568 | /** 569 | * Control how requests made by different elements are synchronized. 570 | * 571 | * Accepts a CSS selector, optionally followed by a colon and a {@link HxSync sync strategy}. 572 | * @see https://htmx.org/attributes/hx-sync/ 573 | */ 574 | ["hx-sync"]?: HxSync | "this" | "closest " | AnyStr; 575 | /** 576 | * Force elements to validate themselves before a request. 577 | * @see https://htmx.org/attributes/hx-validate/ 578 | */ 579 | ["hx-validate"]?: "true"; 580 | /** 581 | * Adds values dynamically to the parameters to submit with the request. 582 | * @deprecated superseded by `hx-vals` 583 | */ 584 | ["hx-vars"]?: AnyStr; 585 | /** 586 | * The URL of the SSE server. 587 | * @see https://htmx.org/extensions/server-sent-events/ 588 | * @category Extensions 589 | */ 590 | ["sse-connect"]?: string; 591 | /** 592 | * The name of the message to swap into the DOM. 593 | * @see https://htmx.org/extensions/server-sent-events/ 594 | * @category Extensions 595 | */ 596 | ["sse-swap"]?: string; 597 | /** 598 | * A URL to establish a WebSocket connection against. 599 | * @see https://htmx.org/extensions/web-sockets/ 600 | * @category Extensions 601 | */ 602 | ["ws-connect"]?: string; 603 | /** 604 | * Sends a message to the nearest websocket based on the trigger value for the element. 605 | * @see https://htmx.org/extensions/web-sockets/ 606 | * @category Extensions 607 | */ 608 | ["ws-send"]?: boolean; 609 | /** 610 | * Apply class transitions on this element. 611 | * @see https://htmx.org/extensions/class-tools/ 612 | * @category Extensions 613 | */ 614 | ["classes"]?: "add " | "remove " | "toggle " | AnyStr; 615 | /** 616 | * The element or elements to disable during requests. 617 | * Accepts CSS selectors. 618 | * @see https://htmx.org/extensions/disable-element1.9.6: / 619 | * @deprecated 1.9.6: superseded by [`hx-disabled-elt`] 620 | * @category Extensions 621 | * 622 | * [`hx-disabled-elt`]: https://htmx.org/attributes/hx-disabled-elt/ 623 | */ 624 | ["hx-disable-element"]?: "self" | AnyStr; 625 | /** 626 | * The strategy for merging new head content. 627 | * @see https://htmx.org/extensions/head-support/ 628 | * @category Extensions 629 | */ 630 | ["hx-head"]?: "merge" | "append" | "re-eval"; 631 | /** 632 | * The ID of a Mustache `