@backtickjs/jsx-react 0.0.0-stage → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +103 -2
- package/dist/index.d.ts +7 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx-runtime.d.ts +23 -0
- package/dist/jsx-runtime.js +48 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/plugin.d.ts +11 -0
- package/dist/plugin.js +28 -0
- package/dist/plugin.js.map +1 -0
- package/package.json +88 -4
- package/src/index.ts +21 -0
- package/src/jsx-runtime.ts +79 -0
- package/src/plugin.ts +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jean-François Bisson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,104 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @backtickjs/jsx-react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React's JSX for [Backtick](https://backtickjs.com): a JSX runtime that type-checks the JSX in your server components against React's own types, and a compile plugin that runs React's JSX transform on each script at build time. React itself is imported for the client, as React Native and any other package is: `import { useState } from "react" with { backtick: "client" }`. React is a peer dependency: this package is built against React 19.2.3, and works with any later 19.x.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install react @backtickjs/jsx-react @backtickjs/core @backtickjs/bundler @backtickjs/node-plugin
|
|
9
|
+
npm install -D @types/react @backtickjs/tsc
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The [repository's README](https://github.com/backtickjs/backtick#packages) says what each package is.
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
In `tsconfig.json`, point JSX at this package and keep imports as written:
|
|
17
|
+
|
|
18
|
+
```jsonc
|
|
19
|
+
{
|
|
20
|
+
"compilerOptions": {
|
|
21
|
+
"verbatimModuleSyntax": true,
|
|
22
|
+
"jsx": "react-jsx",
|
|
23
|
+
"jsxImportSource": "@backtickjs/jsx-react",
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`verbatimModuleSyntax` keeps the import of a name you only splice, such as a client component from another file, which TypeScript would otherwise drop as unused.
|
|
29
|
+
|
|
30
|
+
In `package.json`, name the compile plugin:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"backtick": {
|
|
35
|
+
"plugins": ["@backtickjs/jsx-react/plugin"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Run your server with `node --import @backtickjs/node-plugin server/index.tsx`.
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
A client component, with React's hook imported for the client, and a server component that draws it beside a server value:
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
// server/Home.tsx
|
|
48
|
+
import { cs } from "@backtickjs/core";
|
|
49
|
+
import { useState } from "react" with { backtick: "client" };
|
|
50
|
+
|
|
51
|
+
const Counter = cs`() => {
|
|
52
|
+
const [count, setCount] = $useState(0);
|
|
53
|
+
return (
|
|
54
|
+
<button onClick={() => setCount(count + 1)}>Clicked {count} times</button>
|
|
55
|
+
);
|
|
56
|
+
}`;
|
|
57
|
+
|
|
58
|
+
export async function Home() {
|
|
59
|
+
const assembledAt = new Date().toLocaleTimeString();
|
|
60
|
+
return cs`(
|
|
61
|
+
<main>
|
|
62
|
+
<$Counter />
|
|
63
|
+
<p>Your server assembled this page at {$assembledAt}.</p>
|
|
64
|
+
</main>
|
|
65
|
+
)`;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
On each request, bundle it for the React versions the page loads, and serve the result as a `<script type="module">`, with an import map that resolves `react`, `react/jsx-runtime` and `react-dom/client`:
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { bundler } from "@backtickjs/bundler";
|
|
73
|
+
import { cs } from "@backtickjs/core";
|
|
74
|
+
import { createRoot } from "react-dom/client" with { backtick: "client" };
|
|
75
|
+
import { Home } from "./Home.js";
|
|
76
|
+
|
|
77
|
+
const element = <Home />;
|
|
78
|
+
const bundle = await bundler.build({
|
|
79
|
+
input: cs`$createRoot(document.getElementById("app")!).render($element)`,
|
|
80
|
+
modules: {
|
|
81
|
+
react: "19.2.3",
|
|
82
|
+
"react/jsx-runtime": "19.2.3",
|
|
83
|
+
"react-dom/client": "19.2.3",
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
const { code } = bundle.generate({ format: "es" });
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## What it exports
|
|
90
|
+
|
|
91
|
+
- `@backtickjs/jsx-react/jsx-runtime` and `@backtickjs/jsx-react/jsx-dev-runtime`: the JSX runtime for server components, and its `JSX` namespace.
|
|
92
|
+
- `@backtickjs/jsx-react/plugin`: the compile plugin, React's JSX transform, by TypeScript's own `react-jsx` emit.
|
|
93
|
+
|
|
94
|
+
## Related
|
|
95
|
+
|
|
96
|
+
- [`@backtickjs/core`](https://github.com/backtickjs/backtick/tree/main/packages/core): the `cs` tag
|
|
97
|
+
- [`@backtickjs/bundler`](https://github.com/backtickjs/backtick/tree/main/packages/bundler): builds bundles
|
|
98
|
+
- [`@backtickjs/jsx-solid`](https://github.com/backtickjs/backtick/tree/main/packages/jsx-solid): Solid's JSX
|
|
99
|
+
- [Thinking in Backtick](https://backtickjs.com/docs/thinking-in-backtick/)
|
|
100
|
+
- [How it works](https://backtickjs.com/docs/how-it-works/)
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type JSX } from "./jsx-runtime.js";
|
|
2
|
+
/**
|
|
3
|
+
* What the JSX transform calls, from this package's root, for a tag whose
|
|
4
|
+
* `key` follows a spread, so it can't pass the key apart: `jsx`, with the key
|
|
5
|
+
* and the children taken out of the props.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createElement(type: JSX.ElementType, config: object | null, ...children: unknown[]): JSX.Element;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx } from "./jsx-runtime.js";
|
|
2
|
+
/**
|
|
3
|
+
* What the JSX transform calls, from this package's root, for a tag whose
|
|
4
|
+
* `key` follows a spread, so it can't pass the key apart: `jsx`, with the key
|
|
5
|
+
* and the children taken out of the props.
|
|
6
|
+
*/
|
|
7
|
+
export function createElement(type, config, ...children) {
|
|
8
|
+
const { key, ...props } = (config ?? {});
|
|
9
|
+
if (children.length > 0) {
|
|
10
|
+
props.children = children.length === 1 ? children[0] : children;
|
|
11
|
+
}
|
|
12
|
+
return jsx(type, props, key);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAqB,EACrB,MAAqB,EACrB,GAAG,QAAmB;IAEtB,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,CAGtC,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAClE,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Client, type JsxElementOf, type JsxElementTypeOf, type Spliceable } from "@backtickjs/core";
|
|
2
|
+
import type { JSX as React, ReactElement, ReactNode } from "react";
|
|
3
|
+
export declare namespace JSX {
|
|
4
|
+
type Element = JsxElementOf<React.Element>;
|
|
5
|
+
type ElementType = JsxElementTypeOf<ReactNode> | React.ElementType;
|
|
6
|
+
type ElementClass = React.ElementClass;
|
|
7
|
+
type ElementAttributesProperty = React.ElementAttributesProperty;
|
|
8
|
+
type ElementChildrenAttribute = React.ElementChildrenAttribute;
|
|
9
|
+
type LibraryManagedAttributes<C, P> = React.LibraryManagedAttributes<C, P>;
|
|
10
|
+
type IntrinsicAttributes = React.IntrinsicAttributes;
|
|
11
|
+
type IntrinsicClassAttributes<T> = React.IntrinsicClassAttributes<T>;
|
|
12
|
+
type IntrinsicElements = React.IntrinsicElements;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `<>` and `<Fragment>` on the host: its children as React's fragment's, each
|
|
16
|
+
* an argument rather than an array, so React asks none of them for a key.
|
|
17
|
+
*/
|
|
18
|
+
export declare function Fragment(props: {
|
|
19
|
+
children?: Spliceable;
|
|
20
|
+
}): Client<ReactElement>;
|
|
21
|
+
export declare function jsx(type: JSX.ElementType, props: unknown, key?: string | number): JSX.Element;
|
|
22
|
+
export declare const jsxs: typeof jsx;
|
|
23
|
+
export declare const jsxDEV: typeof jsx;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createJsxElement, cs, } from "@backtickjs/core";
|
|
2
|
+
const createElement = { "@backtickjs": "ClientImport", name: "createElement", from: "react", version: "19.2.3" };
|
|
3
|
+
const ReactFragment = { "@backtickjs": "ClientImport", name: "Fragment", from: "react", version: "19.2.3" };
|
|
4
|
+
const $module0 = {
|
|
5
|
+
id: "2qc9pgkww5cp4:45:9",
|
|
6
|
+
code: "(module, exports, require) => {\n\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.default = (($splice0, $splice1, $splice2) => $splice0()($splice1(), null, ...$splice2()));\n}",
|
|
7
|
+
map: "{\"version\":3,\"file\":\"module.jsx\",\"mappings\":\";;;mBA4CY,4CAAc,CACtB,UAAc,EACd,IAAI,EACJ,GAAI,UAAyB,CAC9B\",\"names\":[],\"ignoreList\":[],\"sources\":[\"@backtickjs/jsx-react/src/jsx-runtime.ts\"]}",
|
|
8
|
+
dependencies: [],
|
|
9
|
+
params: [{ kind: "splice", bindings: [] }, { kind: "splice", bindings: [] }, { kind: "splice", bindings: [] }],
|
|
10
|
+
kind: "expression"
|
|
11
|
+
};
|
|
12
|
+
const $module1 = {
|
|
13
|
+
id: "2qc9pgkww5cp4:70:9",
|
|
14
|
+
code: "(module, exports, require) => {\n\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.default = (($splice0, $splice1, $splice2, $splice3) => $splice0()($splice1(), { key: $splice2() }, $splice3()));\n}",
|
|
15
|
+
map: "{\"version\":3,\"file\":\"module.jsx\",\"mappings\":\";;;mBAqEY,sDAAc,CACtB,UAAc,EACd,EAAE,GAAG,EAAE,UAAI,EAAE,EACb,UAAQ,CACT\",\"names\":[],\"ignoreList\":[],\"sources\":[\"@backtickjs/jsx-react/src/jsx-runtime.ts\"]}",
|
|
16
|
+
dependencies: [],
|
|
17
|
+
params: [{ kind: "splice", bindings: [] }, { kind: "splice", bindings: [] }, { kind: "splice", bindings: [] }, { kind: "splice", bindings: [] }],
|
|
18
|
+
kind: "expression"
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* `<>` and `<Fragment>` on the host: its children as React's fragment's, each
|
|
22
|
+
* an argument rather than an array, so React asks none of them for a key.
|
|
23
|
+
*/
|
|
24
|
+
export function Fragment(props) {
|
|
25
|
+
const children = props.children === undefined
|
|
26
|
+
? []
|
|
27
|
+
: Array.isArray(props.children)
|
|
28
|
+
? props.children
|
|
29
|
+
: [props.children];
|
|
30
|
+
return cs.create($module0, [createElement, ReactFragment, children]);
|
|
31
|
+
}
|
|
32
|
+
// A server component's element: expanded on the host when a bundle is built,
|
|
33
|
+
// what it answers standing where it stood. Hooks belong to client components,
|
|
34
|
+
// as in React: a server component's output is a node, never rendered as one.
|
|
35
|
+
// Its `key`, which the JSX transform passes apart from the props, keys a
|
|
36
|
+
// fragment around that node, so React can tell a list's entries apart.
|
|
37
|
+
export function jsx(type, props, key) {
|
|
38
|
+
// Core's element, as this package types it: a stand-in for a React element.
|
|
39
|
+
const element = createJsxElement(type, props);
|
|
40
|
+
if (key === undefined) {
|
|
41
|
+
return element;
|
|
42
|
+
}
|
|
43
|
+
return cs.create($module1, [createElement, ReactFragment, key, element]);
|
|
44
|
+
}
|
|
45
|
+
export const jsxs = jsx;
|
|
46
|
+
// What a development build of the JSX transform calls, as Bun does.
|
|
47
|
+
export const jsxDEV = jsx;
|
|
48
|
+
//# sourceMappingURL=jsx-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsx-runtime.js","sourceRoot":"","sources":["../src/jsx-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,EAAE,GAKH,MAAM,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;AAwB1B;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,KAExB;IACC,MAAM,QAAQ,GACZ,KAAK,CAAC,QAAQ,KAAK,SAAS;QAC1B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC7B,CAAC,CAAC,KAAK,CAAC,QAAQ;YAChB,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzB,qEAIG;AACL,CAAC;AAED,6EAA6E;AAC7E,8EAA8E;AAC9E,6EAA6E;AAC7E,yEAAyE;AACzE,uEAAuE;AACvE,MAAM,UAAU,GAAG,CACjB,IAAqB,EACrB,KAAc,EACd,GAAqB;IAErB,4EAA4E;IAC5E,MAAM,OAAO,GAAG,gBAAgB,CAC9B,IAAiC,EACjC,KAAK,CACoB,CAAC;IAC5B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,iEAIU,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,CAAC;AACxB,oEAAoE;AACpE,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React's JSX transform as a build's compile step: a script's module, its JSX
|
|
3
|
+
* as calls of `react/jsx-runtime`'s `jsx`, written by TypeScript's own
|
|
4
|
+
* `react-jsx` emit. Typed by its shape, which is the compiler's `Plugin`, so
|
|
5
|
+
* this package needn't depend on the compiler.
|
|
6
|
+
*/
|
|
7
|
+
export declare function react(): (code: string, id: string) => {
|
|
8
|
+
code: string;
|
|
9
|
+
map: string;
|
|
10
|
+
};
|
|
11
|
+
export default react;
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
/**
|
|
3
|
+
* React's JSX transform as a build's compile step: a script's module, its JSX
|
|
4
|
+
* as calls of `react/jsx-runtime`'s `jsx`, written by TypeScript's own
|
|
5
|
+
* `react-jsx` emit. Typed by its shape, which is the compiler's `Plugin`, so
|
|
6
|
+
* this package needn't depend on the compiler.
|
|
7
|
+
*/
|
|
8
|
+
export function react() {
|
|
9
|
+
return (code, id) => {
|
|
10
|
+
const { outputText, sourceMapText } = ts.transpileModule(code, {
|
|
11
|
+
fileName: id,
|
|
12
|
+
compilerOptions: {
|
|
13
|
+
jsx: ts.JsxEmit.ReactJSX,
|
|
14
|
+
module: ts.ModuleKind.ESNext,
|
|
15
|
+
target: ts.ScriptTarget.ESNext,
|
|
16
|
+
sourceMap: true,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
code: outputText.replace(/\n\/\/# sourceMappingURL=.*$/, ""),
|
|
21
|
+
map: sourceMapText,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// What a project names to compile its scripts for React, in its `package.json`:
|
|
26
|
+
// `"backtick": { "plugins": ["@backtickjs/jsx-react/plugin"] }`.
|
|
27
|
+
export default react;
|
|
28
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B;;;;;GAKG;AACH,MAAM,UAAU,KAAK;IAInB,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE;QAClB,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE;YAC7D,QAAQ,EAAE,EAAE;YACZ,eAAe,EAAE;gBACf,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ;gBACxB,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM;gBAC5B,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM;gBAC9B,SAAS,EAAE,IAAI;aAChB;SACF,CAAC,CAAC;QACH,OAAO;YACL,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,8BAA8B,EAAE,EAAE,CAAC;YAC5D,GAAG,EAAE,aAAc;SACpB,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,iEAAiE;AACjE,eAAe,KAAK,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,90 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backtickjs/jsx-react",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.11",
|
|
4
|
+
"description": "React's JSX for Backtick: a JSX runtime for server components, and the compile plugin for scripts",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"backtick",
|
|
7
|
+
"react-native",
|
|
8
|
+
"server-driven-ui",
|
|
9
|
+
"server-components",
|
|
10
|
+
"react",
|
|
11
|
+
"jsx-runtime"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://backtickjs.com",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/backtickjs/backtick/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/backtickjs/backtick.git",
|
|
20
|
+
"directory": "packages/jsx-react"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Jean-François Bisson <jf@backtickjs.com>",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./jsx-runtime": {
|
|
31
|
+
"types": "./dist/jsx-runtime.d.ts",
|
|
32
|
+
"default": "./dist/jsx-runtime.js"
|
|
33
|
+
},
|
|
34
|
+
"./jsx-dev-runtime": {
|
|
35
|
+
"types": "./dist/jsx-runtime.d.ts",
|
|
36
|
+
"default": "./dist/jsx-runtime.js"
|
|
37
|
+
},
|
|
38
|
+
"./plugin": {
|
|
39
|
+
"types": "./dist/plugin.d.ts",
|
|
40
|
+
"default": "./dist/plugin.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"!dist/._cache",
|
|
46
|
+
"src"
|
|
47
|
+
],
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"typecheck": "backtick-tsc -p tsconfig.build.json --noEmit",
|
|
53
|
+
"build": "tspc -b --noCheck --force",
|
|
54
|
+
"dev": "tspc -b --noCheck --watch",
|
|
55
|
+
"test": "node --test \"test/**/*.test.ts\"",
|
|
56
|
+
"format": "prettier --write --ignore-unknown .",
|
|
57
|
+
"format:check": "prettier --check --ignore-unknown .",
|
|
58
|
+
"clean": "rm -rf node_modules dist .turbo",
|
|
59
|
+
"prepack": "cp ../../LICENSE ."
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@backtickjs/core": "^0.1.11"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"react": "^19.2.3",
|
|
66
|
+
"@types/react": "^19.2.18",
|
|
67
|
+
"typescript": "^6.0.3"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@backtickjs/bundler": "^0.1.11",
|
|
71
|
+
"@backtickjs/compiler": "^0.1.11",
|
|
72
|
+
"@backtickjs/prettier-plugin": "^0.1.11",
|
|
73
|
+
"@backtickjs/tsc": "^0.1.11",
|
|
74
|
+
"@backtickjs/tspatch-plugin": "^0.1.11",
|
|
75
|
+
"@types/react": "19.2.18",
|
|
76
|
+
"@types/react-dom": "19.2.7",
|
|
77
|
+
"global-jsdom": "^30.0.0",
|
|
78
|
+
"jsdom": "^30.0.1",
|
|
79
|
+
"prettier": "^3.6.2",
|
|
80
|
+
"react": "19.2.3",
|
|
81
|
+
"react-dom": "19.2.3",
|
|
82
|
+
"ts-patch": "^4.0.1",
|
|
83
|
+
"typescript": "^6.0.3"
|
|
84
|
+
},
|
|
85
|
+
"prettier": {
|
|
86
|
+
"plugins": [
|
|
87
|
+
"@backtickjs/prettier-plugin"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type JSX, jsx } from "./jsx-runtime.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What the JSX transform calls, from this package's root, for a tag whose
|
|
5
|
+
* `key` follows a spread, so it can't pass the key apart: `jsx`, with the key
|
|
6
|
+
* and the children taken out of the props.
|
|
7
|
+
*/
|
|
8
|
+
export function createElement(
|
|
9
|
+
type: JSX.ElementType,
|
|
10
|
+
config: object | null,
|
|
11
|
+
...children: unknown[]
|
|
12
|
+
): JSX.Element {
|
|
13
|
+
const { key, ...props } = (config ?? {}) as {
|
|
14
|
+
key?: string | number;
|
|
15
|
+
[prop: string]: unknown;
|
|
16
|
+
};
|
|
17
|
+
if (children.length > 0) {
|
|
18
|
+
props.children = children.length === 1 ? children[0] : children;
|
|
19
|
+
}
|
|
20
|
+
return jsx(type, props, key);
|
|
21
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createJsxElement,
|
|
3
|
+
cs,
|
|
4
|
+
type Client,
|
|
5
|
+
type JsxElementOf,
|
|
6
|
+
type JsxElementTypeOf,
|
|
7
|
+
type Spliceable,
|
|
8
|
+
} from "@backtickjs/core";
|
|
9
|
+
import type { JSX as React, ReactElement, ReactNode } from "react";
|
|
10
|
+
import { createElement, Fragment as ReactFragment } from "react" with {
|
|
11
|
+
backtick: "client",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// JSX as React types it, for a host's JSX and a script's alike: an element is
|
|
15
|
+
// also a client value, so a host hands one over, and a tag may also name a
|
|
16
|
+
// server component, which answers a script.
|
|
17
|
+
export declare namespace JSX {
|
|
18
|
+
export type Element = JsxElementOf<React.Element>;
|
|
19
|
+
export type ElementType = JsxElementTypeOf<ReactNode> | React.ElementType;
|
|
20
|
+
export type ElementClass = React.ElementClass;
|
|
21
|
+
export type ElementAttributesProperty = React.ElementAttributesProperty;
|
|
22
|
+
export type ElementChildrenAttribute = React.ElementChildrenAttribute;
|
|
23
|
+
export type LibraryManagedAttributes<C, P> = React.LibraryManagedAttributes<
|
|
24
|
+
C,
|
|
25
|
+
P
|
|
26
|
+
>;
|
|
27
|
+
export type IntrinsicAttributes = React.IntrinsicAttributes;
|
|
28
|
+
export type IntrinsicClassAttributes<T> = React.IntrinsicClassAttributes<T>;
|
|
29
|
+
export type IntrinsicElements = React.IntrinsicElements;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `<>` and `<Fragment>` on the host: its children as React's fragment's, each
|
|
34
|
+
* an argument rather than an array, so React asks none of them for a key.
|
|
35
|
+
*/
|
|
36
|
+
export function Fragment(props: {
|
|
37
|
+
children?: Spliceable;
|
|
38
|
+
}): Client<ReactElement> {
|
|
39
|
+
const children: readonly Spliceable[] =
|
|
40
|
+
props.children === undefined
|
|
41
|
+
? []
|
|
42
|
+
: Array.isArray(props.children)
|
|
43
|
+
? props.children
|
|
44
|
+
: [props.children];
|
|
45
|
+
return cs`$createElement(
|
|
46
|
+
$ReactFragment,
|
|
47
|
+
null,
|
|
48
|
+
...($children as ReactNode[]),
|
|
49
|
+
)`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// A server component's element: expanded on the host when a bundle is built,
|
|
53
|
+
// what it answers standing where it stood. Hooks belong to client components,
|
|
54
|
+
// as in React: a server component's output is a node, never rendered as one.
|
|
55
|
+
// Its `key`, which the JSX transform passes apart from the props, keys a
|
|
56
|
+
// fragment around that node, so React can tell a list's entries apart.
|
|
57
|
+
export function jsx(
|
|
58
|
+
type: JSX.ElementType,
|
|
59
|
+
props: unknown,
|
|
60
|
+
key?: string | number,
|
|
61
|
+
): JSX.Element {
|
|
62
|
+
// Core's element, as this package types it: a stand-in for a React element.
|
|
63
|
+
const element = createJsxElement(
|
|
64
|
+
type as (props: never) => unknown,
|
|
65
|
+
props,
|
|
66
|
+
) as unknown as JSX.Element;
|
|
67
|
+
if (key === undefined) {
|
|
68
|
+
return element;
|
|
69
|
+
}
|
|
70
|
+
return cs`$createElement(
|
|
71
|
+
$ReactFragment,
|
|
72
|
+
{ key: $key },
|
|
73
|
+
$element,
|
|
74
|
+
)` as JSX.Element;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const jsxs = jsx;
|
|
78
|
+
// What a development build of the JSX transform calls, as Bun does.
|
|
79
|
+
export const jsxDEV = jsx;
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* React's JSX transform as a build's compile step: a script's module, its JSX
|
|
5
|
+
* as calls of `react/jsx-runtime`'s `jsx`, written by TypeScript's own
|
|
6
|
+
* `react-jsx` emit. Typed by its shape, which is the compiler's `Plugin`, so
|
|
7
|
+
* this package needn't depend on the compiler.
|
|
8
|
+
*/
|
|
9
|
+
export function react(): (
|
|
10
|
+
code: string,
|
|
11
|
+
id: string,
|
|
12
|
+
) => { code: string; map: string } {
|
|
13
|
+
return (code, id) => {
|
|
14
|
+
const { outputText, sourceMapText } = ts.transpileModule(code, {
|
|
15
|
+
fileName: id,
|
|
16
|
+
compilerOptions: {
|
|
17
|
+
jsx: ts.JsxEmit.ReactJSX,
|
|
18
|
+
module: ts.ModuleKind.ESNext,
|
|
19
|
+
target: ts.ScriptTarget.ESNext,
|
|
20
|
+
sourceMap: true,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
code: outputText.replace(/\n\/\/# sourceMappingURL=.*$/, ""),
|
|
25
|
+
map: sourceMapText!,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// What a project names to compile its scripts for React, in its `package.json`:
|
|
31
|
+
// `"backtick": { "plugins": ["@backtickjs/jsx-react/plugin"] }`.
|
|
32
|
+
export default react;
|