# Enabling JSX in Node.js
By default, the native Node.js module system:
- Refuses to consider
.jsx
or.tsx
files to be importable modules - Doesn't know how to transpile JSX syntax into JavaScript
Using immaculata
, you can:
- Make Node.js recognize
.jsx
and.tsx
files as valid modules - Tell Node.js how to transform JSX/TSX into valid JavaScript
- Remap the default
react/jsx-runtime
to another module
import { compileJsx } from "immaculata/hooks.js"
import { registerHooks } from "module"
import ts from 'typescript'
import { fileURLToPath } from "url"
// transpiles tsx into javascript when Node.js loads it
registerHooks(compileJsx((str, url) =>
ts.transpileModule(str, {
fileName: fileURLToPath(url),
compilerOptions: {
target: ts.ScriptTarget.ESNext,
module: ts.ModuleKind.ESNext,
jsx: ts.JsxEmit.ReactJSX,
sourceMap: true,
inlineSourceMap: true,
inlineSources: true,
}
}).outputText
))
# Remapping JSX implementation
By default, using JSX will auto-import react/jsx-runtime
like usual.
You'll almost definitely want to remap that import to anything else:
import { hooks } from "immaculata"
import { registerHooks } from "module"
registerHooks(hooks.mapImport('react/jsx-runtime', 'another-jsx-impl'))
# Simple JSX string-builder
The module 'immaculata/jsx-strings.js'
provides
react/jsx-runtime
-compatible exports that are
implemented as a highly efficient HTML string builder.
import { hooks } from "immaculata"
import { registerHooks } from "module"
registerHooks(hooks.mapImport('react/jsx-runtime', 'immaculata/jsx-strings.js'))
# Using your own JSX implementation
To use a JSX implementatoin within a FileTree, prepend its root
:
import { FileTree, hooks } from "immaculata"
import { registerHooks } from "module"
const tree = new FileTree('site', import.meta.url)
registerHooks(hooks.mapImport('react/jsx-runtime', tree.root + '/my-jsx.ts'))
# Importing with .js
To allow importing .jsx/.tsx
files but using the .js
extension:
import { hooks } from "immaculata"
import { registerHooks } from "module"
registerHooks(hooks.tryAltExts)
import('./foo.js') // now works even though only foo.tsx exists
# JSX Types
If you're not using a library that provides JSX types, you'll need to add your own.
Here's a basic starter:
declare namespace JSX {
type IntrinsicElements = Record<string, any>
interface ElementChildrenAttribute {
children: any
}
}
If you're using immaculata/jsx-strings.js
then you should use this:
declare namespace JSX {
type jsxChildren =
| string
| false
| null
| undefined
| jsxChildren[]
type ElementChildrenAttribute = { children: jsxChildren }
type Element = string
type ElementType =
| string
| ((data: any) => jsxChildren)
}
For HTML tag autocompletion and good-enough attr validation, add this:
declare namespace JSX {
// ...
type jsxify<T extends HTMLElement> = {
[A in keyof T as A extends string ? Lowercase<Exclude<A, 'children'>> : never]?: (
| string
| boolean
| (T[A] extends (string | boolean | null | number)
? T[A]
: never))
} & { children?: any, class?: string }
type IntrinsicElements =
& { [K in keyof HTMLElementTagNameMap]: jsxify<HTMLElementTagNameMap[K]> }
& { meta: jsxify<HTMLMetaElement> & { charset?: 'utf-8' } }
}