@xyd-js/source-react-runtime 0.0.0-build-23166ce-20260423151359
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/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +121 -0
- package/__fixtures__/-1.vite-lib.custom-property/input/package.json +8 -0
- package/__fixtures__/-1.vite-lib.custom-property/input/src/UserCard.tsx +27 -0
- package/__fixtures__/-1.vite-lib.custom-property/input/src/index.ts +1 -0
- package/__fixtures__/-1.vite-lib.custom-property/input/tsconfig.json +12 -0
- package/__fixtures__/-1.vite-lib.custom-property/input/vite.config.ts +22 -0
- package/__fixtures__/-1.vite-lib.custom-property/output.js +10 -0
- package/__fixtures__/1.vite-lib.user-card/input/package.json +8 -0
- package/__fixtures__/1.vite-lib.user-card/input/src/UserCard.tsx +27 -0
- package/__fixtures__/1.vite-lib.user-card/input/src/index.ts +1 -0
- package/__fixtures__/1.vite-lib.user-card/input/tsconfig.json +12 -0
- package/__fixtures__/1.vite-lib.user-card/input/vite.config.ts +22 -0
- package/__fixtures__/1.vite-lib.user-card/output.js +10 -0
- package/__fixtures__/2.vite-lib.sample-app/input/package.json +8 -0
- package/__fixtures__/2.vite-lib.sample-app/input/src/components/UserProfile.tsx +42 -0
- package/__fixtures__/2.vite-lib.sample-app/input/src/contexts/UserContext.tsx +27 -0
- package/__fixtures__/2.vite-lib.sample-app/input/src/index.ts +3 -0
- package/__fixtures__/2.vite-lib.sample-app/input/src/types/user.ts +18 -0
- package/__fixtures__/2.vite-lib.sample-app/input/tsconfig.json +12 -0
- package/__fixtures__/2.vite-lib.sample-app/input/vite.config.ts +22 -0
- package/__fixtures__/2.vite-lib.sample-app/output.js +27 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/package.json +8 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/components/AddTodoForm.tsx +51 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/components/FilterBar.tsx +56 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/components/StatsPanel.tsx +44 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/components/TodoItem.tsx +54 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/index.ts +6 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/types/todo.ts +23 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/tsconfig.json +12 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/vite.config.ts +22 -0
- package/__fixtures__/3.vite-lib.sample-real-app/output.js +63 -0
- package/__fixtures__/4.vite-app.user-card/input/package.json +8 -0
- package/__fixtures__/4.vite-app.user-card/input/src/UserCard.tsx +27 -0
- package/__fixtures__/4.vite-app.user-card/input/src/index.ts +1 -0
- package/__fixtures__/4.vite-app.user-card/input/tsconfig.json +12 -0
- package/__fixtures__/4.vite-app.user-card/input/vite.config.ts +23 -0
- package/__fixtures__/4.vite-app.user-card/output.js +10 -0
- package/__fixtures__/5.rollup.user-card/input/package.json +8 -0
- package/__fixtures__/5.rollup.user-card/input/rollup.config.mjs +20 -0
- package/__fixtures__/5.rollup.user-card/input/src/UserCard.tsx +27 -0
- package/__fixtures__/5.rollup.user-card/input/src/index.ts +1 -0
- package/__fixtures__/5.rollup.user-card/input/tsconfig.json +12 -0
- package/__fixtures__/5.rollup.user-card/output.js +11 -0
- package/__fixtures__/6.esbuild.user-card/input/esbuild.config.mjs +19 -0
- package/__fixtures__/6.esbuild.user-card/input/package.json +8 -0
- package/__fixtures__/6.esbuild.user-card/input/src/UserCard.tsx +27 -0
- package/__fixtures__/6.esbuild.user-card/input/src/index.ts +1 -0
- package/__fixtures__/6.esbuild.user-card/input/tsconfig.json +12 -0
- package/__fixtures__/6.esbuild.user-card/output.js +11 -0
- package/__fixtures__/7.react-router.app/input/app/components/ProductCard.tsx +26 -0
- package/__fixtures__/7.react-router.app/input/app/entry.server.tsx +36 -0
- package/__fixtures__/7.react-router.app/input/app/root.tsx +23 -0
- package/__fixtures__/7.react-router.app/input/app/routes/cart.tsx +16 -0
- package/__fixtures__/7.react-router.app/input/app/routes/home.tsx +29 -0
- package/__fixtures__/7.react-router.app/input/app/routes/product.tsx +6 -0
- package/__fixtures__/7.react-router.app/input/app/routes.ts +7 -0
- package/__fixtures__/7.react-router.app/input/app/types/product.ts +12 -0
- package/__fixtures__/7.react-router.app/input/package.json +8 -0
- package/__fixtures__/7.react-router.app/input/react-router.config.ts +2 -0
- package/__fixtures__/7.react-router.app/input/tsconfig.json +13 -0
- package/__fixtures__/7.react-router.app/input/vite.config.ts +14 -0
- package/__fixtures__/7.react-router.app/output.js +44 -0
- package/__fixtures__/8.tanstack-router.app/input/index.html +5 -0
- package/__fixtures__/8.tanstack-router.app/input/package.json +8 -0
- package/__fixtures__/8.tanstack-router.app/input/src/components/EmployeeTable.tsx +45 -0
- package/__fixtures__/8.tanstack-router.app/input/src/main.tsx +19 -0
- package/__fixtures__/8.tanstack-router.app/input/src/routeTree.gen.ts +77 -0
- package/__fixtures__/8.tanstack-router.app/input/src/routes/__root.tsx +13 -0
- package/__fixtures__/8.tanstack-router.app/input/src/routes/employees.tsx +23 -0
- package/__fixtures__/8.tanstack-router.app/input/src/routes/index.tsx +5 -0
- package/__fixtures__/8.tanstack-router.app/input/src/types/employee.ts +13 -0
- package/__fixtures__/8.tanstack-router.app/input/tsconfig.json +12 -0
- package/__fixtures__/8.tanstack-router.app/input/vite.config.ts +21 -0
- package/__fixtures__/8.tanstack-router.app/output.js +63 -0
- package/__tests__/source-react-runtime.test.ts +61 -0
- package/__tests__/utils.ts +100 -0
- package/dist/esbuild.d.ts +20 -0
- package/dist/esbuild.js +378 -0
- package/dist/esbuild.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +348 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
- package/src/esbuild.ts +45 -0
- package/src/index.ts +437 -0
- package/src/json-schema-to-uniform.ts +108 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsup.json +6 -0
- package/tsup.config.ts +23 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import type { Product } from "../types/product";
|
|
3
|
+
import { ProductCard } from "../components/ProductCard";
|
|
4
|
+
|
|
5
|
+
const PRODUCTS: Product[] = [
|
|
6
|
+
{ id: "1", name: "Keyboard", price: 79, category: "peripherals", inStock: true },
|
|
7
|
+
{ id: "2", name: "Mouse", price: 49, category: "peripherals", inStock: true },
|
|
8
|
+
{ id: "3", name: "Monitor", price: 399, category: "displays", inStock: false },
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export default function Home() {
|
|
12
|
+
const [cart, setCart] = useState<string[]>([]);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div>
|
|
16
|
+
<h1>Shop</h1>
|
|
17
|
+
<p>{cart.length} items in cart</p>
|
|
18
|
+
<div>
|
|
19
|
+
{PRODUCTS.map(p => (
|
|
20
|
+
<ProductCard
|
|
21
|
+
key={p.id}
|
|
22
|
+
product={p}
|
|
23
|
+
onAddToCart={(id) => setCart(prev => [...prev, id])}
|
|
24
|
+
/>
|
|
25
|
+
))}
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"outDir": "./dist",
|
|
4
|
+
"jsx": "react-jsx",
|
|
5
|
+
"skipLibCheck": true,
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"target": "ES2020",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"rootDirs": [".", "./.react-router/types"]
|
|
11
|
+
},
|
|
12
|
+
"include": ["app/**/*.ts", "app/**/*.tsx"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { reactRouter } from "@react-router/dev/vite";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { xydSourceReactRuntime } from "@xyd-js/source-react-runtime";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [
|
|
8
|
+
xydSourceReactRuntime(),
|
|
9
|
+
reactRouter(),
|
|
10
|
+
],
|
|
11
|
+
build: {
|
|
12
|
+
minify: false,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// === client/assets/home-HASH.js ===
|
|
2
|
+
import { p as jsxRuntimeExports, t as Link, w as withComponentProps, a as reactExports } from "./chunk-EVOBXE3Y-HASH.js";
|
|
3
|
+
function ProductCard({ product, onAddToCart, layout = "grid" }) {
|
|
4
|
+
return jsxRuntimeExports.jsxs("div", { className: `product-card product-card--${layout}`, children: [jsxRuntimeExports.jsx(Link, { to: `/products/${product.id}`, children: jsxRuntimeExports.jsx("h3", { children: product.name }) }), jsxRuntimeExports.jsxs("span", { children: ["$", product.price] }), jsxRuntimeExports.jsx("button", { type: "button", disabled: !product.inStock, onClick: () => onAddToCart(product.id), children: product.inStock ? "Add to Cart" : "Out of Stock" })] });
|
|
5
|
+
}
|
|
6
|
+
ProductCard.__xydUniform = JSON.parse('{"title":"ProductCard","canonical":"","description":"","definitions":[{"title":"Props","properties":[{"name":"product","type":"object","description":"","meta":[{"name":"required","value":"true"}],"properties":[{"name":"id","type":"string","description":"","meta":[{"name":"required","value":"true"}]},{"name":"name","type":"string","description":"","meta":[{"name":"required","value":"true"}]},{"name":"price","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"category","type":"string","description":"","meta":[{"name":"required","value":"true"}]},{"name":"inStock","type":"boolean","description":"","meta":[{"name":"required","value":"true"}]}]},{"name":"layout","type":"$xor","description":"","properties":[{"name":"layout","type":"object","description":"","meta":[]},{"name":"layout","type":"object","description":"","meta":[]}],"meta":[]}],"meta":[{"name":"type","value":"parameters"}]}],"examples":{"groups":[]}}');
|
|
7
|
+
const PRODUCTS = [{
|
|
8
|
+
id: "1",
|
|
9
|
+
name: "Keyboard",
|
|
10
|
+
price: 79,
|
|
11
|
+
category: "peripherals",
|
|
12
|
+
inStock: true
|
|
13
|
+
}, {
|
|
14
|
+
id: "2",
|
|
15
|
+
name: "Mouse",
|
|
16
|
+
price: 49,
|
|
17
|
+
category: "peripherals",
|
|
18
|
+
inStock: true
|
|
19
|
+
}, {
|
|
20
|
+
id: "3",
|
|
21
|
+
name: "Monitor",
|
|
22
|
+
price: 399,
|
|
23
|
+
category: "displays",
|
|
24
|
+
inStock: false
|
|
25
|
+
}];
|
|
26
|
+
const home = withComponentProps(function Home() {
|
|
27
|
+
const [cart, setCart] = reactExports.useState([]);
|
|
28
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
29
|
+
children: [/* @__PURE__ */ jsxRuntimeExports.jsx("h1", {
|
|
30
|
+
children: "Shop"
|
|
31
|
+
}), /* @__PURE__ */ jsxRuntimeExports.jsxs("p", {
|
|
32
|
+
children: [cart.length, " items in cart"]
|
|
33
|
+
}), /* @__PURE__ */ jsxRuntimeExports.jsx("div", {
|
|
34
|
+
children: PRODUCTS.map((p) => /* @__PURE__ */ jsxRuntimeExports.jsx(ProductCard, {
|
|
35
|
+
product: p,
|
|
36
|
+
onAddToCart: (id) => setCart((prev) => [...prev, id])
|
|
37
|
+
}, p.id))
|
|
38
|
+
})]
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
export {
|
|
42
|
+
home as default
|
|
43
|
+
};
|
|
44
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Link } from "@tanstack/react-router";
|
|
2
|
+
import type { Employee, SearchFilters } from "../types/employee";
|
|
3
|
+
|
|
4
|
+
interface EmployeeTableProps {
|
|
5
|
+
employees: Employee[];
|
|
6
|
+
filters: SearchFilters;
|
|
7
|
+
onFiltersChange: (filters: SearchFilters) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function EmployeeTable({ employees, filters, onFiltersChange }: EmployeeTableProps) {
|
|
11
|
+
const filtered = employees.filter(e => {
|
|
12
|
+
if (filters.activeOnly && !e.isActive) return false;
|
|
13
|
+
if (filters.department && e.department !== filters.department) return false;
|
|
14
|
+
if (filters.query && !e.name.toLowerCase().includes(filters.query.toLowerCase())) return false;
|
|
15
|
+
return true;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div>
|
|
20
|
+
<input
|
|
21
|
+
value={filters.query}
|
|
22
|
+
onChange={e => onFiltersChange({ ...filters, query: e.target.value })}
|
|
23
|
+
placeholder="Search..."
|
|
24
|
+
/>
|
|
25
|
+
<table>
|
|
26
|
+
<thead>
|
|
27
|
+
<tr><th>Name</th><th>Department</th><th>Salary</th></tr>
|
|
28
|
+
</thead>
|
|
29
|
+
<tbody>
|
|
30
|
+
{filtered.map(emp => (
|
|
31
|
+
<tr key={emp.id}>
|
|
32
|
+
<td>
|
|
33
|
+
<Link to="/employees/$employeeId" params={{ employeeId: String(emp.id) }}>
|
|
34
|
+
{emp.name}
|
|
35
|
+
</Link>
|
|
36
|
+
</td>
|
|
37
|
+
<td>{emp.department}</td>
|
|
38
|
+
<td>${emp.salary.toLocaleString()}</td>
|
|
39
|
+
</tr>
|
|
40
|
+
))}
|
|
41
|
+
</tbody>
|
|
42
|
+
</table>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { StrictMode } from "react";
|
|
2
|
+
import ReactDOM from "react-dom/client";
|
|
3
|
+
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
|
4
|
+
import { routeTree } from "./routeTree.gen";
|
|
5
|
+
|
|
6
|
+
const router = createRouter({ routeTree });
|
|
7
|
+
|
|
8
|
+
declare module "@tanstack/react-router" {
|
|
9
|
+
interface Register {
|
|
10
|
+
router: typeof router;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const root = document.getElementById("root")!;
|
|
15
|
+
ReactDOM.createRoot(root).render(
|
|
16
|
+
<StrictMode>
|
|
17
|
+
<RouterProvider router={router} />
|
|
18
|
+
</StrictMode>,
|
|
19
|
+
);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
|
|
5
|
+
// noinspection JSUnusedGlobalSymbols
|
|
6
|
+
|
|
7
|
+
// This file was automatically generated by TanStack Router.
|
|
8
|
+
// You should NOT make any changes in this file as it will be overwritten.
|
|
9
|
+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
|
10
|
+
|
|
11
|
+
import { Route as rootRouteImport } from './routes/__root'
|
|
12
|
+
import { Route as EmployeesRouteImport } from './routes/employees'
|
|
13
|
+
import { Route as IndexRouteImport } from './routes/index'
|
|
14
|
+
|
|
15
|
+
const EmployeesRoute = EmployeesRouteImport.update({
|
|
16
|
+
id: '/employees',
|
|
17
|
+
path: '/employees',
|
|
18
|
+
getParentRoute: () => rootRouteImport,
|
|
19
|
+
} as any)
|
|
20
|
+
const IndexRoute = IndexRouteImport.update({
|
|
21
|
+
id: '/',
|
|
22
|
+
path: '/',
|
|
23
|
+
getParentRoute: () => rootRouteImport,
|
|
24
|
+
} as any)
|
|
25
|
+
|
|
26
|
+
export interface FileRoutesByFullPath {
|
|
27
|
+
'/': typeof IndexRoute
|
|
28
|
+
'/employees': typeof EmployeesRoute
|
|
29
|
+
}
|
|
30
|
+
export interface FileRoutesByTo {
|
|
31
|
+
'/': typeof IndexRoute
|
|
32
|
+
'/employees': typeof EmployeesRoute
|
|
33
|
+
}
|
|
34
|
+
export interface FileRoutesById {
|
|
35
|
+
__root__: typeof rootRouteImport
|
|
36
|
+
'/': typeof IndexRoute
|
|
37
|
+
'/employees': typeof EmployeesRoute
|
|
38
|
+
}
|
|
39
|
+
export interface FileRouteTypes {
|
|
40
|
+
fileRoutesByFullPath: FileRoutesByFullPath
|
|
41
|
+
fullPaths: '/' | '/employees'
|
|
42
|
+
fileRoutesByTo: FileRoutesByTo
|
|
43
|
+
to: '/' | '/employees'
|
|
44
|
+
id: '__root__' | '/' | '/employees'
|
|
45
|
+
fileRoutesById: FileRoutesById
|
|
46
|
+
}
|
|
47
|
+
export interface RootRouteChildren {
|
|
48
|
+
IndexRoute: typeof IndexRoute
|
|
49
|
+
EmployeesRoute: typeof EmployeesRoute
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare module '@tanstack/react-router' {
|
|
53
|
+
interface FileRoutesByPath {
|
|
54
|
+
'/employees': {
|
|
55
|
+
id: '/employees'
|
|
56
|
+
path: '/employees'
|
|
57
|
+
fullPath: '/employees'
|
|
58
|
+
preLoaderRoute: typeof EmployeesRouteImport
|
|
59
|
+
parentRoute: typeof rootRouteImport
|
|
60
|
+
}
|
|
61
|
+
'/': {
|
|
62
|
+
id: '/'
|
|
63
|
+
path: '/'
|
|
64
|
+
fullPath: '/'
|
|
65
|
+
preLoaderRoute: typeof IndexRouteImport
|
|
66
|
+
parentRoute: typeof rootRouteImport
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const rootRouteChildren: RootRouteChildren = {
|
|
72
|
+
IndexRoute: IndexRoute,
|
|
73
|
+
EmployeesRoute: EmployeesRoute,
|
|
74
|
+
}
|
|
75
|
+
export const routeTree = rootRouteImport
|
|
76
|
+
._addFileChildren(rootRouteChildren)
|
|
77
|
+
._addFileTypes<FileRouteTypes>()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createRootRoute, Outlet, Link } from "@tanstack/react-router";
|
|
2
|
+
|
|
3
|
+
export const Route = createRootRoute({
|
|
4
|
+
component: () => (
|
|
5
|
+
<div>
|
|
6
|
+
<nav>
|
|
7
|
+
<Link to="/">Home</Link>
|
|
8
|
+
<Link to="/employees">Employees</Link>
|
|
9
|
+
</nav>
|
|
10
|
+
<Outlet />
|
|
11
|
+
</div>
|
|
12
|
+
),
|
|
13
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { EmployeeTable } from "../components/EmployeeTable";
|
|
4
|
+
import type { Employee, SearchFilters } from "../types/employee";
|
|
5
|
+
|
|
6
|
+
const EMPLOYEES: Employee[] = [
|
|
7
|
+
{ id: 1, name: "Alice", department: "engineering", salary: 120000, isActive: true },
|
|
8
|
+
{ id: 2, name: "Bob", department: "design", salary: 95000, isActive: true },
|
|
9
|
+
{ id: 3, name: "Carol", department: "marketing", salary: 85000, isActive: false },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export const Route = createFileRoute("/employees")({
|
|
13
|
+
component: function Employees() {
|
|
14
|
+
const [filters, setFilters] = useState<SearchFilters>({ query: "", activeOnly: false });
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div>
|
|
18
|
+
<h1>Employees</h1>
|
|
19
|
+
<EmployeeTable employees={EMPLOYEES} filters={filters} onFiltersChange={setFilters} />
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface Employee {
|
|
2
|
+
id: number;
|
|
3
|
+
name: string;
|
|
4
|
+
department: "engineering" | "design" | "marketing" | "sales";
|
|
5
|
+
salary: number;
|
|
6
|
+
isActive: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface SearchFilters {
|
|
10
|
+
query: string;
|
|
11
|
+
department?: Employee["department"];
|
|
12
|
+
activeOnly: boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import react from "@vitejs/plugin-react";
|
|
4
|
+
import { TanStackRouterVite } from "@tanstack/router-plugin/vite";
|
|
5
|
+
import { xydSourceReactRuntime } from "@xyd-js/source-react-runtime";
|
|
6
|
+
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [
|
|
9
|
+
xydSourceReactRuntime(),
|
|
10
|
+
TanStackRouterVite({ target: "react", autoCodeSplitting: true }),
|
|
11
|
+
react(),
|
|
12
|
+
],
|
|
13
|
+
build: {
|
|
14
|
+
outDir: "dist",
|
|
15
|
+
minify: false,
|
|
16
|
+
rollupOptions: {
|
|
17
|
+
input: resolve(__dirname, "index.html"),
|
|
18
|
+
external: ["react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-dom", "@tanstack/react-router"],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// === assets/employees-HASH.js ===
|
|
2
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
5
|
+
import { Link } from "@tanstack/react-router";
|
|
6
|
+
function EmployeeTable({ employees, filters, onFiltersChange }) {
|
|
7
|
+
const filtered = employees.filter((e) => {
|
|
8
|
+
if (filters.activeOnly && !e.isActive)
|
|
9
|
+
return false;
|
|
10
|
+
if (filters.department && e.department !== filters.department)
|
|
11
|
+
return false;
|
|
12
|
+
if (filters.query && !e.name.toLowerCase().includes(filters.query.toLowerCase()))
|
|
13
|
+
return false;
|
|
14
|
+
return true;
|
|
15
|
+
});
|
|
16
|
+
return jsxs("div", { children: [jsx("input", { value: filters.query, onChange: (e) => onFiltersChange({ ...filters, query: e.target.value }), placeholder: "Search..." }), jsxs("table", { children: [jsx("thead", { children: jsxs("tr", { children: [jsx("th", { children: "Name" }), jsx("th", { children: "Department" }), jsx("th", { children: "Salary" })] }) }), jsx("tbody", { children: filtered.map((emp) => jsxs("tr", { children: [jsx("td", { children: jsx(Link, { to: "/employees/$employeeId", params: { employeeId: String(emp.id) }, children: emp.name }) }), jsx("td", { children: emp.department }), jsxs("td", { children: ["$", emp.salary.toLocaleString()] })] }, emp.id)) })] })] });
|
|
17
|
+
}
|
|
18
|
+
EmployeeTable.__xydUniform = JSON.parse('{"title":"EmployeeTable","canonical":"","description":"","definitions":[{"title":"Props","properties":[{"name":"employees","type":"$array","description":"","meta":[{"name":"required","value":"true"}],"properties":[],"ofProperty":{"name":"","type":"object","properties":[{"name":"id","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"name","type":"string","description":"","meta":[{"name":"required","value":"true"}]},{"name":"department","type":"$xor","description":"","properties":[{"name":"department","type":"object","description":"","meta":[{"name":"required","value":"true"}]},{"name":"department","type":"object","description":"","meta":[{"name":"required","value":"true"}]},{"name":"department","type":"object","description":"","meta":[{"name":"required","value":"true"}]},{"name":"department","type":"object","description":"","meta":[{"name":"required","value":"true"}]}],"meta":[{"name":"required","value":"true"}]},{"name":"salary","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"isActive","type":"boolean","description":"","meta":[{"name":"required","value":"true"}]}],"description":"","meta":[{"name":"required","value":"true"}]}},{"name":"filters","type":"object","description":"","meta":[{"name":"required","value":"true"}],"properties":[{"name":"query","type":"string","description":"","meta":[{"name":"required","value":"true"}]},{"name":"department","type":"$xor","description":"","properties":[{"name":"department","type":"object","description":"","meta":[]},{"name":"department","type":"object","description":"","meta":[]},{"name":"department","type":"object","description":"","meta":[]},{"name":"department","type":"object","description":"","meta":[]}],"meta":[]},{"name":"activeOnly","type":"boolean","description":"","meta":[{"name":"required","value":"true"}]}]}],"meta":[{"name":"type","value":"parameters"}]}],"examples":{"groups":[]}}');
|
|
19
|
+
const EMPLOYEES = [{
|
|
20
|
+
id: 1,
|
|
21
|
+
name: "Alice",
|
|
22
|
+
department: "engineering",
|
|
23
|
+
salary: 12e4,
|
|
24
|
+
isActive: true
|
|
25
|
+
}, {
|
|
26
|
+
id: 2,
|
|
27
|
+
name: "Bob",
|
|
28
|
+
department: "design",
|
|
29
|
+
salary: 95e3,
|
|
30
|
+
isActive: true
|
|
31
|
+
}, {
|
|
32
|
+
id: 3,
|
|
33
|
+
name: "Carol",
|
|
34
|
+
department: "marketing",
|
|
35
|
+
salary: 85e3,
|
|
36
|
+
isActive: false
|
|
37
|
+
}];
|
|
38
|
+
const SplitComponent = function Employees() {
|
|
39
|
+
const [filters, setFilters] = useState({
|
|
40
|
+
query: "",
|
|
41
|
+
activeOnly: false
|
|
42
|
+
});
|
|
43
|
+
return /* @__PURE__ */ jsxDEV("div", { children: [
|
|
44
|
+
/* @__PURE__ */ jsxDEV("h1", { children: "Employees" }, void 0, false, {
|
|
45
|
+
fileName: "<ROOT>/src/routes/employees.tsx?tsr-split=component",
|
|
46
|
+
lineNumber: 28,
|
|
47
|
+
columnNumber: 17
|
|
48
|
+
}, this),
|
|
49
|
+
/* @__PURE__ */ jsxDEV(EmployeeTable, { employees: EMPLOYEES, filters, onFiltersChange: setFilters }, void 0, false, {
|
|
50
|
+
fileName: "<ROOT>/src/routes/employees.tsx?tsr-split=component",
|
|
51
|
+
lineNumber: 29,
|
|
52
|
+
columnNumber: 17
|
|
53
|
+
}, this)
|
|
54
|
+
] }, void 0, true, {
|
|
55
|
+
fileName: "<ROOT>/src/routes/employees.tsx?tsr-split=component",
|
|
56
|
+
lineNumber: 27,
|
|
57
|
+
columnNumber: 10
|
|
58
|
+
}, this);
|
|
59
|
+
};
|
|
60
|
+
export {
|
|
61
|
+
SplitComponent as component
|
|
62
|
+
};
|
|
63
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {describe, it} from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {testSourceReactRuntime} from './utils';
|
|
4
|
+
|
|
5
|
+
const tests: {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
propertyName?: string;
|
|
9
|
+
}[] = [
|
|
10
|
+
// advanced/optional
|
|
11
|
+
{
|
|
12
|
+
name: '-1.vite-lib.custom-property',
|
|
13
|
+
description: 'custom propertyName: injects __docs instead of __xydUniform',
|
|
14
|
+
propertyName: '__docs',
|
|
15
|
+
},
|
|
16
|
+
// vite-lib
|
|
17
|
+
{
|
|
18
|
+
name: '1.vite-lib.user-card',
|
|
19
|
+
description: 'vite lib: basic component with inline props',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: '2.vite-lib.sample-app',
|
|
23
|
+
description: 'vite lib: cross-file types with contexts',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: '3.vite-lib.sample-real-app',
|
|
27
|
+
description: 'vite lib: real todo app with multiple components',
|
|
28
|
+
},
|
|
29
|
+
// vite-app
|
|
30
|
+
{
|
|
31
|
+
name: '4.vite-app.user-card',
|
|
32
|
+
description: 'vite app: basic component with automatic JSX runtime',
|
|
33
|
+
},
|
|
34
|
+
// rollup
|
|
35
|
+
{
|
|
36
|
+
name: '5.rollup.user-card',
|
|
37
|
+
description: 'rollup: basic component with @rollup/plugin-typescript',
|
|
38
|
+
},
|
|
39
|
+
// esbuild
|
|
40
|
+
{
|
|
41
|
+
name: '6.esbuild.user-card',
|
|
42
|
+
description: 'esbuild: basic component with esbuild loader',
|
|
43
|
+
},
|
|
44
|
+
// real framework apps
|
|
45
|
+
{
|
|
46
|
+
name: '7.react-router.app',
|
|
47
|
+
description: 'react-router v7: real framework mode with routes, Link, useParams',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: '8.tanstack-router.app',
|
|
51
|
+
description: 'tanstack router: file-based routing with createFileRoute, Link, useNavigate',
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
describe('xyd-source-react-runtime', () => {
|
|
56
|
+
for (const t of tests) {
|
|
57
|
+
it(t.description, async () => {
|
|
58
|
+
await testSourceReactRuntime(t.name, t.propertyName);
|
|
59
|
+
}, 60_000);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import {execSync} from 'node:child_process';
|
|
4
|
+
import {expect} from 'vitest';
|
|
5
|
+
|
|
6
|
+
const fixturesBase = path.resolve(__dirname, '../__fixtures__');
|
|
7
|
+
|
|
8
|
+
function fullFixturePath(name: string) {
|
|
9
|
+
return path.join(fixturesBase, name);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Recursively finds all .js files in a directory, sorted by path.
|
|
14
|
+
*/
|
|
15
|
+
function findJsFiles(dir: string): string[] {
|
|
16
|
+
const files: string[] = [];
|
|
17
|
+
if (!fs.existsSync(dir)) return files;
|
|
18
|
+
|
|
19
|
+
for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
|
|
20
|
+
const full = path.join(dir, entry.name);
|
|
21
|
+
if (entry.isDirectory()) {
|
|
22
|
+
files.push(...findJsFiles(full));
|
|
23
|
+
} else if (entry.name.endsWith('.js')) {
|
|
24
|
+
files.push(full);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return files.sort();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Concatenates all JS files from the build output into a single string,
|
|
32
|
+
* with file separators so you can see the full transformed code.
|
|
33
|
+
*/
|
|
34
|
+
function collectBuildOutput(buildDir: string, inputRoot: string, propertyName: string = '__xydUniform'): string {
|
|
35
|
+
const jsFiles = findJsFiles(buildDir);
|
|
36
|
+
const parts: string[] = [];
|
|
37
|
+
|
|
38
|
+
for (const file of jsFiles) {
|
|
39
|
+
const relativePath = path.relative(buildDir, file);
|
|
40
|
+
const code = fs.readFileSync(file, 'utf-8');
|
|
41
|
+
|
|
42
|
+
if (!code.includes(propertyName)) continue;
|
|
43
|
+
|
|
44
|
+
parts.push(`// === ${relativePath} ===`);
|
|
45
|
+
parts.push(code);
|
|
46
|
+
parts.push('');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return parts.join('\n')
|
|
50
|
+
.split(inputRoot).join('<ROOT>')
|
|
51
|
+
.replace(/[-_\w]+-[A-Za-z0-9_-]{6,10}\.(js|css)/g, (match) => {
|
|
52
|
+
// Normalize content hashes in filenames: home-BpHCO2Vo.js → home-HASH.js
|
|
53
|
+
return match.replace(/-[A-Za-z0-9_-]{6,10}\./, '-HASH.');
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Builds a fixture app by running its `build` script from package.json,
|
|
59
|
+
* then snapshots the full build output code.
|
|
60
|
+
*
|
|
61
|
+
* Each fixture is a real app with its own framework config and build command.
|
|
62
|
+
*/
|
|
63
|
+
export async function testSourceReactRuntime(fixtureName: string, propertyName: string = '__xydUniform') {
|
|
64
|
+
const inputRoot = fullFixturePath(`${fixtureName}/input`);
|
|
65
|
+
|
|
66
|
+
// Clean previous build outputs
|
|
67
|
+
for (const dir of ['dist', 'build', '.react-router']) {
|
|
68
|
+
const p = path.resolve(inputRoot, dir);
|
|
69
|
+
if (fs.existsSync(p)) fs.rmSync(p, {recursive: true, force: true});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Run the fixture's own build script — just like a real consumer would
|
|
73
|
+
execSync('pnpm build', {cwd: inputRoot, stdio: 'pipe', timeout: 60_000});
|
|
74
|
+
|
|
75
|
+
// Find the build output directory (varies by framework)
|
|
76
|
+
let buildDir = path.resolve(inputRoot, 'dist');
|
|
77
|
+
if (!fs.existsSync(buildDir)) {
|
|
78
|
+
buildDir = path.resolve(inputRoot, 'build');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Collect full build output code
|
|
82
|
+
const result = collectBuildOutput(buildDir, inputRoot, propertyName);
|
|
83
|
+
|
|
84
|
+
// Verify at least one component was injected
|
|
85
|
+
expect(result).toContain(propertyName);
|
|
86
|
+
|
|
87
|
+
// Save snapshot
|
|
88
|
+
const snapshotPath = fullFixturePath(`${fixtureName}/output.js`);
|
|
89
|
+
fs.writeFileSync(snapshotPath, result);
|
|
90
|
+
|
|
91
|
+
// Compare
|
|
92
|
+
const expected = fs.readFileSync(snapshotPath, 'utf8');
|
|
93
|
+
expect(result).toEqual(expected);
|
|
94
|
+
|
|
95
|
+
// Cleanup
|
|
96
|
+
for (const dir of ['dist', 'build', '.react-router']) {
|
|
97
|
+
const p = path.resolve(inputRoot, dir);
|
|
98
|
+
if (fs.existsSync(p)) fs.rmSync(p, {recursive: true, force: true});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Plugin } from 'esbuild';
|
|
2
|
+
import { XydSourceReactRuntimeOptions } from './index.js';
|
|
3
|
+
import 'vite';
|
|
4
|
+
import '@xyd-js/uniform';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* esbuild plugin adapter for xyd-source-react-runtime.
|
|
8
|
+
*
|
|
9
|
+
* ```js
|
|
10
|
+
* import {xydSourceReactRuntimeEsbuild} from '@xyd-js/source-react-runtime/esbuild';
|
|
11
|
+
*
|
|
12
|
+
* await esbuild.build({
|
|
13
|
+
* entryPoints: ['src/index.ts'],
|
|
14
|
+
* plugins: [xydSourceReactRuntimeEsbuild({tsconfig: './tsconfig.json'})],
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare function xydSourceReactRuntimeEsbuild(options?: XydSourceReactRuntimeOptions): Plugin;
|
|
19
|
+
|
|
20
|
+
export { xydSourceReactRuntimeEsbuild };
|