diff --git a/.eslintrc b/.eslintrc
index 382fcff6..87e55820 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -97,7 +97,7 @@
"no-prototype-builtins": "error",
"no-redeclare": "error",
"no-shadow": "error",
- "no-undef": "error",
+ "no-undef": "off",
"sort-imports": "off",
},
}
diff --git a/README.md b/README.md
index 72962f6a..25df940e 100644
--- a/README.md
+++ b/README.md
@@ -22,3 +22,13 @@ Why Tuono? Just a badass name.
| - routes/
| | - api/
```
+
+```tsx
+// Data is passed by the loading function
+const IndexPage = ({data, isLoading, isError}) =>
Index Page
+
+export const Route = createFileRoute({
+ loader: (params) => fetchApi(params),
+ component: IndexPage
+})
+```
diff --git a/package.json b/package.json
index ef55793b..551ebac6 100644
--- a/package.json
+++ b/package.json
@@ -2,9 +2,10 @@
"name": "tuono",
"version": "0.1.0",
"description": "",
- "main": "index.js",
+ "main": "src/index.js",
"packageManager": "pnpm@8.12.1",
"scripts": {
+ "dev": "turbo dev",
"build": "turbo build",
"lint": "turbo lint",
"format": "turbo format",
@@ -17,6 +18,7 @@
"devDependencies": {
"@tanstack/config": "^0.7.0",
"@types/node": "^20.12.7",
+ "@types/react": "^18.3.1",
"@typescript-eslint/eslint-plugin": "^7.7.1",
"@typescript-eslint/parser": "^7.7.1",
"@vitejs/plugin-react": "^4.2.1",
@@ -28,7 +30,7 @@
"eslint-plugin-react-hooks": "^4.6.0",
"prettier": "^3.2.4",
"typescript": "^5.4.5",
- "vite": "^5.2.10",
+ "vite": "^5.2.11",
"vitest": "^1.5.2"
}
}
diff --git a/packages/tuono-router/package.json b/packages/tuono-router/package.json
index f714cd13..8a2010ef 100644
--- a/packages/tuono-router/package.json
+++ b/packages/tuono-router/package.json
@@ -1,12 +1,44 @@
{
"name": "tuono-router",
- "version": "1.0.0",
+ "version": "0.0.1",
"description": "",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/esm/index.d.ts",
+ "default": "./dist/esm/index.js"
+ },
+ "require": {
+ "types": "./dist/cjs/index.d.cts",
+ "default": "./dist/cjs/index.cjs"
+ }
+ },
+ "./package.json": "./package.json"
},
+ "sideEffects": false,
+ "scripts": {
+ "dev": "vite build --watch",
+ "build": "vite build",
+ "lint": "eslint --ext .ts,.tsx ./src -c ../../.eslintrc",
+ "format": "prettier -u --write '**/*'",
+ "test": "vitest --watch=false",
+ "types": "tsc --noEmit"
+ },
+ "peerDependencies": {
+ "react": ">=16.3.0",
+ "react-dom": ">=16.3.0"
+ },
+ "type": "module",
+ "types": "dist/esm/index.d.ts",
+ "main": "dist/cjs/index.cjs",
+ "module": "dist/esm/index.js",
"keywords": [],
"author": "",
- "license": "MIT"
+ "license": "MIT",
+ "devDependencies": {
+ "jsdom": "^24.0.0"
+ },
+ "dependencies": {
+ "zustand": "4.4.7"
+ }
}
diff --git a/packages/tuono-router/src/components/Link.tsx b/packages/tuono-router/src/components/Link.tsx
new file mode 100644
index 00000000..3e938ad5
--- /dev/null
+++ b/packages/tuono-router/src/components/Link.tsx
@@ -0,0 +1,18 @@
+import { useRouterStore } from '../hooks/useRouterStore'
+import type { AnchorHTMLAttributes, MouseEvent } from 'react'
+
+export default function Link(
+ props: AnchorHTMLAttributes,
+): JSX.Element {
+ const handleTransition = (e: MouseEvent): void => {
+ e.preventDefault()
+ props.onClick?.(e)
+ useRouterStore.setState({ location: { pathname: props.href || '' } })
+ window.history.pushState('', '', props.href)
+ }
+ return (
+
+ {props.children}
+
+ )
+}
diff --git a/packages/tuono-router/src/components/Matches.tsx b/packages/tuono-router/src/components/Matches.tsx
new file mode 100644
index 00000000..c86d24fc
--- /dev/null
+++ b/packages/tuono-router/src/components/Matches.tsx
@@ -0,0 +1,30 @@
+import * as React from 'react'
+import { useRouter } from '../hooks/useRouter'
+import { useRouterStore } from '../hooks/useRouterStore'
+
+export function Matches(): JSX.Element | undefined {
+ const matchId = useRouterStore.getState().matches[0]?.id
+
+ //if (!matchId) return <>>
+
+ return (
+
+
+
+ )
+}
+
+interface MatchProps {
+ id: string
+}
+
+const Match = React.memo(function ({ id }: MatchProps) {
+ const location = useRouterStore((st) => st.location)
+ const router = useRouter()
+
+ console.log(router, location)
+
+ const route = router.routesById[location?.pathname]
+
+ return route.component()
+})
diff --git a/packages/tuono-router/src/components/Outlet.tsx b/packages/tuono-router/src/components/Outlet.tsx
new file mode 100644
index 00000000..6617d4a6
--- /dev/null
+++ b/packages/tuono-router/src/components/Outlet.tsx
@@ -0,0 +1,7 @@
+import * as React from 'react'
+
+const Outlet = React.memo(function Outlet() {
+ return Outlet
+})
+
+export default Outlet
diff --git a/packages/tuono-router/src/components/RouterContext.tsx b/packages/tuono-router/src/components/RouterContext.tsx
new file mode 100644
index 00000000..1b0595a8
--- /dev/null
+++ b/packages/tuono-router/src/components/RouterContext.tsx
@@ -0,0 +1,20 @@
+import * as React from 'react'
+import type { Router } from '../router'
+
+const routerContext = React.createContext(null!)
+
+const ROUTER_DOM_CONTEXT = '__TUONO_ROUTER__CONTEXT__'
+
+export function getRouterContext(): any {
+ if (typeof document === 'undefined') {
+ return routerContext
+ }
+
+ if (window[ROUTER_DOM_CONTEXT as any]) {
+ return window[ROUTER_DOM_CONTEXT as any]
+ }
+
+ window[ROUTER_DOM_CONTEXT as any] = routerContext as any
+
+ return routerContext
+}
diff --git a/packages/tuono-router/src/components/RouterProvider.tsx b/packages/tuono-router/src/components/RouterProvider.tsx
new file mode 100644
index 00000000..6cc3d16c
--- /dev/null
+++ b/packages/tuono-router/src/components/RouterProvider.tsx
@@ -0,0 +1,60 @@
+import * as React from 'react'
+import { getRouterContext } from './RouterContext'
+import { Matches } from './Matches'
+
+type Router = any
+
+interface RouterContextProviderProps {
+ router: Router
+ children: ReactNode
+}
+
+function RouterContextProvider({
+ router,
+ children,
+ ...rest
+}: RouterContextProviderProps): JSX.Element {
+ // Allow the router to update options on the router instance
+ router.update({
+ ...router.options,
+ ...rest,
+ context: {
+ ...router.options.context,
+ ...rest.context,
+ },
+ })
+
+ const routerContext = getRouterContext()
+
+ const pendingElement = router.options.defaultPendingComponent ? (
+
+ ) : null
+
+ const provider = (
+
+ {children}
+
+ )
+
+ // NOTE: verify usefulness
+ if (router.options.Wrap) {
+ return {provider}
+ }
+
+ return provider
+}
+
+interface RouterProviderProps {
+ router: Router
+}
+
+export function RouterProvider({
+ router,
+ ...rest
+}: RouterProviderProps): JSX.Element {
+ return (
+
+
+
+ )
+}
diff --git a/packages/tuono-router/src/hooks/useRouter.tsx b/packages/tuono-router/src/hooks/useRouter.tsx
new file mode 100644
index 00000000..b200a6a9
--- /dev/null
+++ b/packages/tuono-router/src/hooks/useRouter.tsx
@@ -0,0 +1,7 @@
+import * as React from 'react'
+import { getRouterContext } from '../components/RouterContext'
+import type { Router } from '../router'
+
+export function useRouter(): Router {
+ return React.useContext(getRouterContext())
+}
diff --git a/packages/tuono-router/src/hooks/useRouterStore.tsx b/packages/tuono-router/src/hooks/useRouterStore.tsx
new file mode 100644
index 00000000..0c80cbda
--- /dev/null
+++ b/packages/tuono-router/src/hooks/useRouterStore.tsx
@@ -0,0 +1,40 @@
+import * as React from 'react'
+import { create } from 'zustand'
+
+export interface ParsedLocation {
+ href: string
+ pathname: string
+ search: Record
+ searchStr: string
+ hash: string
+}
+
+interface RouterState {
+ isLoading: boolean
+ isTransitioning: boolean
+ status: 'idle'
+ location?: ParsedLocation
+ matches: string[]
+ pendingMatches: string[]
+ cachedMatches: string[]
+ statusCode: 200
+ updateLocation: (loc: ParsedLocation) => void
+}
+
+export const useRouterStore = create()((set) => ({
+ isLoading: false,
+ isTransitioning: false,
+ status: 'idle',
+ location: {
+ href: '',
+ pathname: '/',
+ search: {},
+ searchStr: '',
+ hash: '',
+ },
+ matches: [],
+ pendingMatches: [],
+ cachedMatches: [],
+ statusCode: 200,
+ updateLocation: (location: ParsedLocation): void => set({ location }),
+}))
diff --git a/packages/tuono-router/src/index.ts b/packages/tuono-router/src/index.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/packages/tuono-router/src/index.tsx b/packages/tuono-router/src/index.tsx
new file mode 100644
index 00000000..84ec4f37
--- /dev/null
+++ b/packages/tuono-router/src/index.tsx
@@ -0,0 +1,4 @@
+export { RouterProvider } from './components/RouterProvider'
+export { default as Link } from './components/Link'
+export { createRouter } from './router'
+export { createRoute, createRootRoute, getRouteApi } from './route'
diff --git a/packages/tuono-router/src/route.ts b/packages/tuono-router/src/route.ts
new file mode 100644
index 00000000..82450fe0
--- /dev/null
+++ b/packages/tuono-router/src/route.ts
@@ -0,0 +1,119 @@
+import type { ReactNode } from 'react'
+import type { RouterType } from './router'
+import { trimPathLeft, joinPaths } from './utils'
+
+interface RouteOptions {
+ isRoot?: boolean
+ getParentRoute?: () => Route
+ path?: string
+ component: ReactNode
+}
+
+export function createRoute(options: RouteOptions): Route {
+ return new Route(options)
+}
+
+export const rootRouteId = '__root__'
+
+export class Route {
+ parentRoute!: any
+ id: number
+ fullPath!: string
+ path: string
+ options: any
+
+ children?: Route[]
+ router: RouterType
+ isRoot: boolean
+ originalIndex: number
+ component: ReactNode
+
+ constructor(options: RouteOptions) {
+ this.isRoot = options.isRoot || !options.getParentRoute
+ this.options = options
+ ;(this as any).$$typeof = Symbol.for('react.memo')
+
+ this.component = options.component
+ }
+
+ init = (originalIndex: number): void => {
+ this.originalIndex = originalIndex
+
+ const isRoot = !this.options?.path && !this.options?.id
+
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ this.parentRoute = this.options?.getParentRoute?.()
+
+ if (isRoot) {
+ this.path = rootRouteId as TPath
+ }
+
+ let path: undefined | string = isRoot ? rootRouteId : this.options.path
+
+ // If the path is anything other than an index path, trim it up
+ if (path && path !== '/') {
+ path = trimPathLeft(path)
+ }
+
+ const customId = this.options?.id || path
+
+ // Strip the parentId prefix from the first level of children
+ let id = isRoot
+ ? rootRouteId
+ : joinPaths([
+ this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id,
+ customId,
+ ])
+
+ if (path === rootRouteId) {
+ path = '/'
+ }
+
+ if (id !== rootRouteId) {
+ id = joinPaths(['/', id])
+ }
+
+ const fullPath =
+ id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path])
+
+ this.path = path as TPath
+ this.id = id as TId
+ // this.customId = customId as TCustomId
+ this.fullPath = fullPath as TFullPath
+ this.to = fullPath as TrimPathRight
+ }
+
+ addChildren(routes: Route[]): Route {
+ this.children = routes
+ return this
+ }
+
+ update = (options: RouteOptions): this => {
+ Object.assign(this.options, options)
+ return this
+ }
+
+ useRouteContext = () => {}
+
+ useParams = () => {}
+}
+
+export function createRootRoute(options?: RouteOptions): Route {
+ return new Route({ ...options, isRoot: true })
+}
+
+export function getRouteApi(id: string): RouteApi {
+ return new RouteApi(id)
+}
+
+class RouteApi {
+ id: string
+
+ constructor(id: string) {
+ this.id = id
+ }
+
+ useParams = () => {}
+
+ useRouteContext = () => {}
+}
diff --git a/packages/tuono-router/src/router.ts b/packages/tuono-router/src/router.ts
new file mode 100644
index 00000000..2f7d95e1
--- /dev/null
+++ b/packages/tuono-router/src/router.ts
@@ -0,0 +1,98 @@
+import { trimPath, trimPathRight, trimPathLeft, parsePathname } from './utils'
+import type { Route } from './route'
+
+interface CreateRouter {
+ routeTree: any
+ basePath?: string
+}
+export type RouterType = any
+
+export function createRouter(options: CreateRouterArgs): Router {
+ return new Router(options)
+}
+
+export class Router {
+ options: any
+ basePath = '/'
+ routeTree: any
+ history: any
+ isServer = typeof document === 'undefined'
+ routesById = {}
+ routesByPath = {}
+ flatRoutes: any
+
+ constructor(options: CreateRouter) {
+ this.update({
+ ...options,
+ })
+
+ if (!this.isServer) {
+ ;(window as any).__TSR__ROUTER__ = this
+ }
+ }
+
+ update = (newOptions: CreateRouter): void => {
+ this.options = {
+ ...this.options,
+ ...newOptions,
+ }
+
+ this.#updateBasePath(newOptions.basePath)
+
+ // NOTE: next iteration
+ this.#historyUpdate()
+
+ // NOTE: next iteration
+ this.#storeUpdate()
+
+ if (this.options.routeTree !== this.routeTree) {
+ this.routeTree = this.options.routeTree
+ this.#buildRouteTree()
+ }
+ }
+
+ #historyUpdate = (): void => {
+ // TODO: update history
+ }
+
+ #storeUpdate = (): void => {
+ // TODO: update store
+ }
+
+ #buildRouteTree = (): void => {
+ const recurseRoutes = (childRoutes: Route[]): void => {
+ childRoutes.forEach((route: Route, i: number) => {
+ route.init(i)
+
+ this.routesById[route.id] = route
+
+ if (!route.isRoot && route.options.path) {
+ const trimmedFullPath = trimPathRight(route.fullPath)
+ if (
+ !this.routesByPath[trimmedFullPath] ||
+ route.fullPath.endsWith('/')
+ ) {
+ this.routesByPath[trimmedFullPath] = route
+ }
+ }
+
+ const children = route.children
+ if (children?.length) {
+ recurseRoutes(children)
+ }
+ })
+ }
+
+ recurseRoutes([this.routeTree])
+ }
+
+ #updateBasePath = (basePath?: string): void => {
+ if (!this.basePath || (basePath && basePath !== basePath)) {
+ if (basePath === undefined || basePath === '' || basePath === '/') {
+ this.basePath = '/'
+ } else {
+ this.basePath = `/${trimPath(basePath)}`
+ }
+ }
+ }
+}
diff --git a/packages/tuono-router/src/types.ts b/packages/tuono-router/src/types.ts
new file mode 100644
index 00000000..3ba2ea71
--- /dev/null
+++ b/packages/tuono-router/src/types.ts
@@ -0,0 +1,4 @@
+export interface Segment {
+ type: 'pathname' | 'param' | 'wildcard'
+ value: string
+}
diff --git a/packages/tuono-router/src/utils.ts b/packages/tuono-router/src/utils.ts
new file mode 100644
index 00000000..08236d6a
--- /dev/null
+++ b/packages/tuono-router/src/utils.ts
@@ -0,0 +1,77 @@
+import type { Segment } from './types'
+
+export function joinPaths(paths: (string | undefined)[]): string {
+ return cleanPath(paths.filter(Boolean).join('/'))
+}
+
+function cleanPath(path: string): string {
+ // remove double slashes
+ return path.replace(/\/{2,}/g, '/')
+}
+
+export function parsePathname(pathname?: string): Segment[] {
+ if (!pathname) {
+ return []
+ }
+
+ pathname = cleanPath(pathname)
+
+ const segments: Segment[] = []
+
+ if (pathname.slice(0, 1) === '/') {
+ pathname = pathname.substring(1)
+ segments.push({
+ type: 'pathname',
+ value: '/',
+ })
+ }
+
+ if (!pathname) {
+ return segments
+ }
+
+ // Remove empty segments and '.' segments
+ const split = pathname.split('/').filter(Boolean)
+
+ segments.push(
+ ...split.map((part): Segment => {
+ if (part === '$' || part === '*') {
+ return {
+ type: 'wildcard',
+ value: part,
+ }
+ }
+
+ if (part.charAt(0) === '$') {
+ return {
+ type: 'param',
+ value: part,
+ }
+ }
+
+ return {
+ type: 'pathname',
+ value: part,
+ }
+ }),
+ )
+
+ if (pathname.slice(-1) === '/') {
+ pathname = pathname.substring(1)
+ segments.push({
+ type: 'pathname',
+ value: '/',
+ })
+ }
+
+ return segments
+}
+export function trimPathLeft(path: string): string {
+ return path === '/' ? path : path.replace(/^\/{1,}/, '')
+}
+export function trimPathRight(path: string): string {
+ return path === '/' ? path : path.replace(/\/{1,}$/, '')
+}
+export function trimPath(path: string): string {
+ return trimPathRight(trimPathLeft(path))
+}
diff --git a/packages/tuono-router/tests/components/RouterProvider.test.tsx b/packages/tuono-router/tests/components/RouterProvider.test.tsx
new file mode 100644
index 00000000..27e7c72e
--- /dev/null
+++ b/packages/tuono-router/tests/components/RouterProvider.test.tsx
@@ -0,0 +1,34 @@
+import { expectTypeOf, test } from 'vitest'
+import {
+ RouterProvider,
+ createRootRoute,
+ createRoute,
+ createRouter,
+} from '../../src'
+
+const rootRoute = createRootRoute()
+
+const indexRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ path: '/',
+})
+
+const invoicesRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ path: '/invoices',
+})
+
+const routeTree = rootRoute.addChildren([invoicesRoute, indexRoute])
+
+const defaultRouter = createRouter({
+ routeTree,
+})
+
+type DefaultRouter = typeof defaultRouter
+
+test('can pass default router to the provider', () => {
+ expectTypeOf(RouterProvider).parameter(0).toMatchTypeOf<{
+ router: DefaultRouter
+ routeTree?: DefaultRouter['routeTree']
+ }>()
+})
diff --git a/packages/tuono-router/tests/route.test.ts b/packages/tuono-router/tests/route.test.ts
new file mode 100644
index 00000000..5284e038
--- /dev/null
+++ b/packages/tuono-router/tests/route.test.ts
@@ -0,0 +1,28 @@
+/* eslint-disable */
+import { describe, it, expect } from 'vitest'
+import { getRouteApi, createRoute } from '../src'
+
+describe('getRouteApi', () => {
+ it('should have the useRouteContext hook', () => {
+ const api = getRouteApi('foo')
+ expect(api.useRouteContext).toBeDefined()
+ })
+
+ it('should have the useParams hook', () => {
+ const api = getRouteApi('foo')
+ expect(api.useParams).toBeDefined()
+ })
+})
+
+describe('createRoute has the same hooks as getRouteApi', () => {
+ const routeApi = getRouteApi('foo')
+ const hookNames = Object.keys(routeApi).filter((key) => key.startsWith('use'))
+ const route = createRoute({} as any)
+
+ it.each(hookNames.map((name) => [name]))(
+ 'should have the "%s" hook defined',
+ (hookName) => {
+ expect(route[hookName as keyof typeof route]).toBeDefined()
+ },
+ )
+})
diff --git a/packages/tuono-router/tsconfig.json b/packages/tuono-router/tsconfig.json
index 01ad9218..eae0e083 100644
--- a/packages/tuono-router/tsconfig.json
+++ b/packages/tuono-router/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
- "jsx": "react",
+ "jsx": "react-jsx",
},
"include": ["src", "tests", "vite.config.ts"],
}
diff --git a/packages/tuono-router/vite.config.ts b/packages/tuono-router/vite.config.ts
index 1c0b0d95..1338feba 100644
--- a/packages/tuono-router/vite.config.ts
+++ b/packages/tuono-router/vite.config.ts
@@ -18,4 +18,3 @@ export default mergeConfig(
srcDir: './src',
}),
)
-
diff --git a/packages/tuono-routes-generator/package.json b/packages/tuono-routes-generator/package.json
index 09fd2a39..9c742167 100644
--- a/packages/tuono-routes-generator/package.json
+++ b/packages/tuono-routes-generator/package.json
@@ -3,6 +3,7 @@
"version": "0.1.0",
"description": "",
"scripts": {
+ "dev": "vite build --watch",
"build": "vite build",
"lint": "eslint --ext .ts,.tsx ./src -c ../../.eslintrc",
"format": "prettier -u --write '**/*'",
@@ -28,7 +29,9 @@
"dist",
"src"
],
- "dependencies": {},
+ "dependencies": {
+ "prettier": "^3.2.4"
+ },
"type": "module",
"types": "dist/esm/index.d.ts",
"main": "dist/cjs/index.cjs",
diff --git a/packages/tuono-routes-generator/src/generator.ts b/packages/tuono-routes-generator/src/generator.ts
index 4a3f7d74..7494dbea 100644
--- a/packages/tuono-routes-generator/src/generator.ts
+++ b/packages/tuono-routes-generator/src/generator.ts
@@ -239,29 +239,40 @@ export async function routeGenerator(config = defaultConfig): Promise {
(d): any => d,
])
+ const imports = [
+ ...sortedRouteNodes.map((node) => {
+ return `import ${node.variableName}Import from './${replaceBackslash(
+ removeExt(
+ path.relative(
+ path.dirname(config.generatedRouteTree),
+ path.resolve(config.folderName, node.filePath),
+ ),
+ false,
+ ),
+ )}'`
+ }),
+ ].join('\n')
+
+ const createRoutes = [
+ ...sortedRouteNodes.map((node) => {
+ return `const ${node.variableName} = createRoute({ component: ${node.variableName}Import })`
+ }),
+ ].join('\n')
+
const routeImports = [
'// This file is auto-generated by Tuono',
+ "import { createRoute } from 'tuono'",
[
- `import { Route as rootRoute } from './${replaceBackslash(
+ `import RootImport from './${replaceBackslash(
path.relative(
path.dirname(config.generatedRouteTree),
path.resolve(config.folderName, ROOT_PATH_ID),
),
)}'`,
- ...sortedRouteNodes.map((node) => {
- return `import { Route as ${
- node.variableName
- }Import } from './${replaceBackslash(
- removeExt(
- path.relative(
- path.dirname(config.generatedRouteTree),
- path.resolve(config.folderName, node.filePath),
- ),
- false,
- ),
- )}'`
- }),
].join('\n'),
+ imports,
+ 'const rootRoute = createRoute({ isRoot: true, component: RootImport });',
+ createRoutes,
'// Create/Update Routes',
sortedRouteNodes
.map((node) => {
@@ -269,7 +280,7 @@ export async function routeGenerator(config = defaultConfig): Promise {
const lazyComponentNode = routePiecesByPath[node.routePath]?.lazy
return [
- `const ${node.variableName}Route = ${node.variableName}Import.update({
+ `const ${node.variableName}Route = ${node.variableName}.update({
${[
`path: '${node.cleanedPath}'`,
`getParentRoute: () => ${node.parent?.variableName ?? 'root'}Route`,
@@ -302,25 +313,6 @@ export async function routeGenerator(config = defaultConfig): Promise {
].join('')
})
.join('\n\n'),
- ...[
- '// Populate the FileRoutesByPath interface',
- `declare module '@tanstack/react-router' {
- interface FileRoutesByPath {
- ${routeNodes
- .map((routeNode) => {
- return `'${removeTrailingUnderscores(routeNode.routePath)}': {
- preLoaderRoute: typeof ${routeNode.variableName}Import
- parentRoute: typeof ${
- routeNode.parent?.variableName
- ? `${routeNode.parent.variableName}Import`
- : 'rootRoute'
- }
- }`
- })
- .join('\n')}
- }
-}`,
- ],
'// Create and export the route tree',
`export const routeTree = rootRoute.addChildren([${routeConfigChildrenText}])`,
]
diff --git a/packages/tuono-routes-generator/tests/generator/multi-level/routeTree.expected.ts b/packages/tuono-routes-generator/tests/generator/multi-level/routeTree.expected.ts
index 28373a38..1b674805 100644
--- a/packages/tuono-routes-generator/tests/generator/multi-level/routeTree.expected.ts
+++ b/packages/tuono-routes-generator/tests/generator/multi-level/routeTree.expected.ts
@@ -1,56 +1,43 @@
// This file is auto-generated by Tuono
-import { Route as rootRoute } from './routes/__root'
-import { Route as AboutImport } from './routes/about'
-import { Route as IndexImport } from './routes/index'
-import { Route as PostsIndexImport } from './routes/posts/index'
-import { Route as PostsPostSlugImport } from './routes/posts/post-slug'
+import { createRoute } from 'tuono'
+
+import RootImport from './routes/__root'
+
+import AboutImport from './routes/about'
+import IndexImport from './routes/index'
+import PostsIndexImport from './routes/posts/index'
+import PostsPostSlugImport from './routes/posts/post-slug'
+
+const rootRoute = createRoute({ isRoot: true, component: RootImport })
+
+const About = createRoute({ component: AboutImport })
+const Index = createRoute({ component: IndexImport })
+const PostsIndex = createRoute({ component: PostsIndexImport })
+const PostsPostSlug = createRoute({ component: PostsPostSlugImport })
// Create/Update Routes
-const AboutRoute = AboutImport.update({
+const AboutRoute = About.update({
path: '/about',
getParentRoute: () => rootRoute,
} as any)
-const IndexRoute = IndexImport.update({
+const IndexRoute = Index.update({
path: '/',
getParentRoute: () => rootRoute,
} as any)
-const PostsIndexRoute = PostsIndexImport.update({
+const PostsIndexRoute = PostsIndex.update({
path: '/posts/',
getParentRoute: () => rootRoute,
} as any)
-const PostsPostSlugRoute = PostsPostSlugImport.update({
+const PostsPostSlugRoute = PostsPostSlug.update({
path: '/posts/post-slug',
getParentRoute: () => rootRoute,
} as any)
-// Populate the FileRoutesByPath interface
-
-declare module '@tanstack/react-router' {
- interface FileRoutesByPath {
- '/': {
- preLoaderRoute: typeof IndexImport
- parentRoute: typeof rootRoute
- }
- '/about': {
- preLoaderRoute: typeof AboutImport
- parentRoute: typeof rootRoute
- }
- '/posts/post-slug': {
- preLoaderRoute: typeof PostsPostSlugImport
- parentRoute: typeof rootRoute
- }
- '/posts/': {
- preLoaderRoute: typeof PostsIndexImport
- parentRoute: typeof rootRoute
- }
- }
-}
-
// Create and export the route tree
export const routeTree = rootRoute.addChildren([
diff --git a/packages/tuono-routes-generator/tests/generator/single-level/routeTree.expected.ts b/packages/tuono-routes-generator/tests/generator/single-level/routeTree.expected.ts
index ac2d0eb0..53eadb1d 100644
--- a/packages/tuono-routes-generator/tests/generator/single-level/routeTree.expected.ts
+++ b/packages/tuono-routes-generator/tests/generator/single-level/routeTree.expected.ts
@@ -1,36 +1,29 @@
// This file is auto-generated by Tuono
-import { Route as rootRoute } from './routes/__root'
-import { Route as AboutImport } from './routes/about'
-import { Route as IndexImport } from './routes/index'
+import { createRoute } from 'tuono'
+
+import RootImport from './routes/__root'
+
+import AboutImport from './routes/about'
+import IndexImport from './routes/index'
+
+const rootRoute = createRoute({ isRoot: true, component: RootImport })
+
+const About = createRoute({ component: AboutImport })
+const Index = createRoute({ component: IndexImport })
// Create/Update Routes
-const AboutRoute = AboutImport.update({
+const AboutRoute = About.update({
path: '/about',
getParentRoute: () => rootRoute,
} as any)
-const IndexRoute = IndexImport.update({
+const IndexRoute = Index.update({
path: '/',
getParentRoute: () => rootRoute,
} as any)
-// Populate the FileRoutesByPath interface
-
-declare module '@tanstack/react-router' {
- interface FileRoutesByPath {
- '/': {
- preLoaderRoute: typeof IndexImport
- parentRoute: typeof rootRoute
- }
- '/about': {
- preLoaderRoute: typeof AboutImport
- parentRoute: typeof rootRoute
- }
- }
-}
-
// Create and export the route tree
export const routeTree = rootRoute.addChildren([IndexRoute, AboutRoute])
diff --git a/packages/tuono-vite/package.json b/packages/tuono-vite/package.json
index 759a8a64..b4b57ae2 100644
--- a/packages/tuono-vite/package.json
+++ b/packages/tuono-vite/package.json
@@ -1,9 +1,12 @@
{
"name": "tuono-vite",
- "version": "0.1.0",
+ "version": "0.0.1",
"description": "",
- "main": "src/index.ts",
+ "main": "dist/cjs/index.cjs",
+ "types": "dist/esm/index.d.ts",
+ "module": "dist/esm/index.js",
"scripts": {
+ "dev": "vite build --watch",
"build": "vite build",
"lint": "eslint --ext .ts,.tsx ./src -c ../../.eslintrc",
"format": "prettier -u --write '**/*'",
diff --git a/packages/tuono-vite/src/index.ts b/packages/tuono-vite/src/index.ts
index fb48d853..78bac2e6 100644
--- a/packages/tuono-vite/src/index.ts
+++ b/packages/tuono-vite/src/index.ts
@@ -40,7 +40,7 @@ export function RouterGenerator(): Plugin {
}
return {
- name: 'vite-plugin-fs-router-generator',
+ name: 'vite-plugin-tuono-fs-router-generator',
configResolved: async (): Promise => {
await generate()
},
@@ -59,7 +59,7 @@ export function RouterCodeSplitter(): Plugin {
const ROOT: string = process.cwd()
return {
- name: 'vite-plugin-fs-router-code-splitter',
+ name: 'vite-plugin-tuono-fs-router-code-splitter',
enforce: 'pre',
resolveId(source): string {
if (source.startsWith(SPLIT_PREFIX + ':')) {
diff --git a/packages/tuono/package.json b/packages/tuono/package.json
index ecc0338b..8febc1d0 100644
--- a/packages/tuono/package.json
+++ b/packages/tuono/package.json
@@ -3,6 +3,7 @@
"version": "0.0.1",
"description": "",
"scripts": {
+ "dev": "vite build --watch",
"build": "vite build",
"lint": "eslint --ext .ts,.tsx ./src -c ../../.eslintrc",
"format": "prettier -u --write '**/*'",
@@ -31,6 +32,9 @@
"src",
"README.md"
],
+ "dependencies": {
+ "tuono-router": "workspace:*"
+ },
"sideEffects": false,
"keywords": [],
"author": "Valerio Ageno",
diff --git a/packages/tuono/src/index.ts b/packages/tuono/src/index.ts
index a1446de0..0afed695 100644
--- a/packages/tuono/src/index.ts
+++ b/packages/tuono/src/index.ts
@@ -1,3 +1,9 @@
-export default function sum(a: number, b: number): number {
- return a + b
-}
+import {
+ createRoute,
+ createRootRoute,
+ createRouter,
+ Link,
+ RouterProvider,
+} from 'tuono-router'
+
+export { createRoute, createRootRoute, createRouter, Link, RouterProvider }
diff --git a/turbo.json b/turbo.json
index 999c8bc0..8d69584b 100644
--- a/turbo.json
+++ b/turbo.json
@@ -5,7 +5,11 @@
"format": {},
"test": {},
"build": {
- "outputs": ["dist/**"]
+ "outputs": ["dist/**"],
+ "dependsOn": ["^build"]
+ },
+ "dev": {
+ "dependsOn": ["^dev"]
}
}
}