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"] } } }