diff --git a/readme.md b/readme.md index 96098b5..6178401 100644 --- a/readme.md +++ b/readme.md @@ -231,6 +231,21 @@ var x = require('xastscript') console.log() ``` +For [TypeScript][], this can be done by setting `"jsx": "react"`, +`"jsxFactory": "x"`, and `"jsxFragmentFactory": "null"` in the compiler options. +For more details on configuring JSX for TypeScript, see the +[TypeScript JSX handbook page][]. + +TypeScript also lets you configure this in a script: + +```tsx +/** @jsx x */ +/** @jsxFrag null */ +import * as x from 'xastscript' + +console.log() +``` + ## Security XML can be a dangerous language: don’t trust user-provided data. @@ -327,3 +342,7 @@ abide by its terms. [babel]: https://github.com/babel/babel [babel-jsx]: https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-react-jsx + +[typescript]: https://www.typescriptlang.org + +[typescript jsx handbook page]: https://www.typescriptlang.org/docs/handbook/jsx.html diff --git a/types/index.d.ts b/types/index.d.ts index 34d047c..a9ed093 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,8 +1,8 @@ -// TypeScript Version: 3.7 +// TypeScript Version: 4.0 -import {Element, Node, Root} from 'xast' +import * as xast from 'xast' -type Children = string | Node | number | Children[] +type Children = string | xast.Node | number | Children[] type Primitive = null | undefined | string | number @@ -17,7 +17,7 @@ type Attributes = Record * @param name Qualified name. Case sensitive and can contain a namespace prefix (such as rdf:RDF). * @param children (Lists of) child nodes. When strings are encountered, they are mapped to Text nodes. */ -declare function xastscript(name: string, ...children: Children[]): Element +declare function xastscript(name: string, ...children: Children[]): xast.Element /** * Create XML trees in xast. @@ -25,7 +25,7 @@ declare function xastscript(name: string, ...children: Children[]): Element * @param name Qualified name. Case sensitive and can contain a namespace prefix (such as rdf:RDF). * @param children (Lists of) child nodes. When strings are encountered, they are mapped to Text nodes. */ -declare function xastscript(name: null, ...children: Children[]): Root +declare function xastscript(name: null, ...children: Children[]): xast.Root /** * Create XML trees in xast. @@ -38,6 +38,58 @@ declare function xastscript( name: string, attributes?: Attributes, ...children: Children[] -): Element +): xast.Element + +/** + * This unique symbol is declared to specify the key on which JSX children are passed, without conflicting + * with the Attributes type. + */ +declare const children: unique symbol + +/** + * This namespace allows to use `xastscript` as a JSX implementation. + * + * This namespace is only used to support the use as JSX. It’s **not** intended for direct usage. + */ +declare namespace xastscript.JSX { + /** + * This defines the return value of JSX syntax. + */ + type Element = xast.Element | xast.Root + + /** + * This disallows the use of functional components. + */ + type IntrinsicAttributes = never + + /** + * This defines the prop types for known elements. + * + * For `xastscript` this defines any string may be used in combination with `xast` `Attributes`. + * + * This **must** be an interface. + */ + // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style + interface IntrinsicElements { + [tagName: string]: + | Attributes + | { + /** + * The prop that matches `ElementChildrenAttribute` key defines the type of JSX children, defines the children type. + */ + [children]?: Children + } + } + + /** + * The key of this interface defines as what prop children are passed. + */ + interface ElementChildrenAttribute { + /** + * Only the key matters, not the value. + */ + [children]?: never + } +} export = xastscript diff --git a/types/test-jsx.tsx b/types/test-jsx.tsx new file mode 100644 index 0000000..68dd089 --- /dev/null +++ b/types/test-jsx.tsx @@ -0,0 +1,44 @@ +import {Element, Root} from 'xast' +import * as x from 'xastscript' + +const xmlns = 'http://www.sitemaps.org/schemas/sitemap/0.9' + +let jsx: Element | Root +jsx = +jsx = +jsx = string +jsx = {['string', 'string']} +jsx = ( + + + +) +jsx = ( + + + string + +) +jsx = ( + + + +) +jsx = ( + + + + +) +jsx = {[, ]} +jsx = {[]} +jsx = <> + +jsx = // $ExpectError +jsx = {{invalid: 'child'}} // $ExpectError + +const element: Element = // $ExpectError +const root: Root = <> // $ExpectError + +declare function Bar(props?: Record): Element +const bar = // $ExpectError diff --git a/types/test.ts b/types/test.ts index 524b9f5..437a9f6 100644 --- a/types/test.ts +++ b/types/test.ts @@ -1,4 +1,4 @@ -import x = require('xastscript') +import * as x from 'xastscript' x('urlset') // $ExpectType Element x('urlset', 'string') // $ExpectType Element diff --git a/types/tsconfig.json b/types/tsconfig.json index bb008c0..5d74cc2 100644 --- a/types/tsconfig.json +++ b/types/tsconfig.json @@ -1,6 +1,9 @@ { "compilerOptions": { "module": "commonjs", + "jsx": "react", + "jsxFactory": "x", + "jsxFragmentFactory": "null", "lib": ["es2015"], "noImplicitAny": true, "noImplicitThis": true,