Skip to content

Commit

Permalink
feat: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Julien-R44 committed May 12, 2024
1 parent 2a46f4c commit 7427ac6
Show file tree
Hide file tree
Showing 24 changed files with 415 additions and 104 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ shrinkwrap.yaml
package-lock.json
todo.md
.adonisjs
playground/public/assets
public/assets
**/*/public/assets
3 changes: 1 addition & 2 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"medium-zoom": "^1.1.0",
"reflect-metadata": "^0.2.2",
"undici": "^6.15.0",
"unpoly": "^3.7.3",
"vite": "^5.2.10"
"unpoly": "^3.7.3"
}
}
9 changes: 5 additions & 4 deletions packages/client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import type { KyInstance } from 'ky'
import { RouteHelper } from './route.js'
import { TuyauRequest } from './request.js'
import type {
TuyauOptions as TuyauOptions,
AdonisClient,
TuyauOptions,
GeneratedRoutes,
TuyauRpcClient,
ApiDefinition,
TuyauClient,
} from './types.js'

const methods = ['get', 'post', 'put', 'delete', 'patch', 'head'] as const
Expand Down Expand Up @@ -70,8 +71,8 @@ function createProxy(options: {
export function createTuyau<const Api extends ApiDefinition>(
options: TuyauOptions<Api>,
): Api['routes'] extends GeneratedRoutes
? RouteHelper<Api['routes']> & AdonisClient<Api['definition']>
: AdonisClient<Api['definition']> {
? TuyauClient<Api['definition'], Api['routes']>
: TuyauRpcClient<Api['definition']> {
const baseUrl = options.baseUrl
const client = ky.create({ prefixUrl: baseUrl, throwHttpErrors: false, ...options })

Expand Down
15 changes: 11 additions & 4 deletions packages/client/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Options as KyOptions } from 'ky'
import type { Simplify, Serialize, IsNever, Prettify } from '@tuyau/utils/types'

import type { RouteHelper } from './route.js'

/**
* Shape of the response returned by Tuyau
*/
Expand Down Expand Up @@ -38,7 +40,7 @@ export type ResponseOrUnwrap<Res extends Record<number, unknown>> = Promise<Tuya
*
* The real implementation of this type is done with a Proxy object
*/
export type AdonisClient<in out Route extends Record<string, any>> = {
export type TuyauRpcClient<in out Route extends Record<string, any>> = {
[K in keyof Route as K extends `:${string}` ? never : K]: Route[K] extends {
response: infer Res extends Record<number, unknown>
request: infer Request
Expand All @@ -60,11 +62,11 @@ export type AdonisClient<in out Route extends Record<string, any>> = {
export type CreateParams<Route extends Record<string, any>> =
Extract<keyof Route, `:${string}`> extends infer Path extends string
? IsNever<Path> extends true
? Prettify<AdonisClient<Route>>
? Prettify<TuyauRpcClient<Route>>
: ((params: {
[param in Path extends `:${infer Param}` ? Param : never]: string | number
}) => Prettify<AdonisClient<Route[Path]>> & CreateParams<Route[Path]>) &
Prettify<AdonisClient<Route>>
}) => Prettify<TuyauRpcClient<Route[Path]>> & CreateParams<Route[Path]>) &
Prettify<TuyauRpcClient<Route>>
: never

export type GeneratedRoutes = readonly {
Expand Down Expand Up @@ -216,3 +218,8 @@ type MaybeArray<T> = T | T[]
* DeepPartial type
*/
export type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T

export type TuyauClient<
Definition extends Record<string, any>,
Routes extends GeneratedRoutes,
> = RouteHelper<Routes> & TuyauRpcClient<Definition>
Empty file removed packages/inertia/index.tsx
Empty file.
58 changes: 56 additions & 2 deletions packages/inertia/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,66 @@
{
"name": "inertia",
"name": "@tuyau/inertia",
"type": "module",
"version": "0.0.0",
"description": "",
"author": "Julien Ripouteau <[email protected]>",
"license": "ISC",
"keywords": [],
"exports": {
".": "./build/index.js",
"./react": "./build/react/index.js",
"./types": "./build/types.js"
},
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"lint": "eslint .",
"typecheck": "tsc --noEmit",
"build": "tsup-node && tsc --emitDeclarationOnly --declaration",
"test": "c8 node --enable-source-maps --loader ts-node/esm bin/test.ts",
"quick:test": "node --enable-source-maps --loader ts-node/esm bin/test.ts",
"checks": "pnpm lint && pnpm typecheck"
},
"peerDependencies": {
"@inertiajs/react": "^1.0.16",
"@inertiajs/vue3": "^1.0.16",
"@tuyau/client": "workspace:*",
"react": "^18.3.1",
"solid-js": "^1.8.17",
"vue": "^3.4.27"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"vue": {
"optional": true
},
"@inertiajs/react": {
"optional": true
},
"@inertiajs/vue3": {
"optional": true
}
},
"devDependencies": {
"@inertiajs/react": "^1.0.16",
"@inertiajs/vue3": "^1.0.16",
"@tuyau/client": "workspace:*",
"@types/react": "^18.3.1",
"react": "^18.3.1",
"solid-js": "^1.8.17",
"vue": "^3.4.27"
},
"tsup": {
"entry": [
"./src/index.ts",
"./src/react/index.tsx",
"./src/types.ts"
],
"outDir": "./build",
"clean": true,
"format": "esm",
"dts": false,
"target": "esnext"
}
}
1 change: 1 addition & 0 deletions packages/inertia/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('hey')
42 changes: 42 additions & 0 deletions packages/inertia/src/react/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react'
import { Link as InertiaLink } from '@inertiajs/react'
import type { TuyauClient, RouteName, RoutesNameParams } from '@tuyau/client'

import type { AssertedApi } from '../types.js'

const TuyauContext = React.createContext<TuyauClient<any, any> | null>(null)

export const TuyauProvider = (props: {
children: React.ReactNode
client: TuyauClient<any, any>
}) => <TuyauContext.Provider value={props.client}>{props.children}</TuyauContext.Provider>

export const useTuyau = () => React.useContext(TuyauContext)

type LinkProps<Route extends RouteName<AssertedApi['routes']>> = Omit<
React.ComponentPropsWithoutRef<typeof InertiaLink>,
'href' | 'method'
> & {
route: Route
params: RoutesNameParams<AssertedApi['routes'], Route>
}

function LinkInner<Route extends RouteName<AssertedApi['routes']>>(
props: LinkProps<Route>,
ref?: React.ForwardedRef<React.ElementRef<typeof InertiaLink>>,
) {
const tuyau = useTuyau()
if (!tuyau) throw new Error('You must wrap your app in a TuyauProvider')

const result = tuyau.$route(props.route, props.params as any)

console.log(result)

return <InertiaLink {...props} href={result.path} method={result.method[0]} ref={ref} />
}

export const Link: <Route extends RouteName<AssertedApi['routes']>>(
props: LinkProps<Route> & {
ref?: React.Ref<React.ElementRef<typeof InertiaLink>>
},
) => ReturnType<typeof LinkInner> = React.forwardRef(LinkInner) as any
13 changes: 13 additions & 0 deletions packages/inertia/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { GeneratedRoutes } from '@tuyau/client'

/**
* To be extended user-side with module augmentation
*/
export interface Api {}

export type AssertedApi = Api extends {
routes: GeneratedRoutes
definition: Record<string, any>
}
? Api
: never
3 changes: 2 additions & 1 deletion packages/inertia/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"extends": "@adonisjs/tsconfig/tsconfig.package.json",
"compilerOptions": {
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"rootDir": "./",
"rootDir": "./src",
"types": [],
"outDir": "./build"
}
Expand Down
3 changes: 2 additions & 1 deletion playgrounds/inertia-react/adonisrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default defineConfig({
| will be scanned automatically from the "./commands" directory.
|
*/
commands: [() => import('@adonisjs/core/commands'), () => import('@adonisjs/lucid/commands')],
commands: [() => import('@adonisjs/core/commands'), () => import('@adonisjs/lucid/commands'), () => import('@tuyau/core/commands')],

Check warning on line 13 in playgrounds/inertia-react/adonisrc.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `()·=>·import('@adonisjs/core/commands'),·()·=>·import('@adonisjs/lucid/commands'),·()·=>·import('@tuyau/core/commands')` with `⏎····()·=>·import('@adonisjs/core/commands'),⏎····()·=>·import('@adonisjs/lucid/commands'),⏎····()·=>·import('@tuyau/core/commands'),⏎··`

/*
|--------------------------------------------------------------------------
Expand Down Expand Up @@ -38,6 +38,7 @@ export default defineConfig({
() => import('@adonisjs/lucid/database_provider'),
() => import('@adonisjs/auth/auth_provider'),
() => import('@adonisjs/inertia/inertia_provider'),
() => import('@tuyau/core/tuyau_provider')

Check warning on line 41 in playgrounds/inertia-react/adonisrc.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `,`
],

/*
Expand Down
49 changes: 49 additions & 0 deletions playgrounds/inertia-react/app/controllers/comments_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { HttpContext } from '@adonisjs/core/http'

export default class CommentsController {
/**
* Display a list of resource
*/
async index({}: HttpContext) {}

/**
* Display form to create a new record
*/
async create({}: HttpContext) {}

/**
* Handle form submission for the create action
*/
async store({}: HttpContext) {}

/**
* Show individual record
*/
async show({}: HttpContext) {}

/**
* Edit individual record
*/
async edit({}: HttpContext) {
return [
{
id: 1,
body: 'Hello world',
user: {
id: 1,
email: '[email protected]',
},
},
]
}

/**
* Handle form submission for the edit action
*/
async update({}: HttpContext) {}

/**
* Delete record
*/
async destroy({}: HttpContext) {}
}
11 changes: 11 additions & 0 deletions playgrounds/inertia-react/app/controllers/inertia_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { HttpContext } from '@adonisjs/core/http'

export default class InertiaController {
async index({ inertia }: HttpContext) {
return inertia.render('home', { version: '6.0' })
}

async backoffice({ inertia }: HttpContext) {
return inertia.render('backoffice', { version: '6.0' })
}
}
40 changes: 40 additions & 0 deletions playgrounds/inertia-react/app/controllers/posts_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { HttpContext } from '@adonisjs/core/http'

export default class PostsController {
/**
* Display a list of resource
*/
async index({}: HttpContext) {}

/**
* Display form to create a new record
*/
async create({ request, inertia }: HttpContext) {

Check failure on line 12 in playgrounds/inertia-react/app/controllers/posts_controller.ts

View workflow job for this annotation

GitHub Actions / lint

'request' is defined but never used. Allowed unused args must match /^_/u
return inertia.render('posts/create')
}

/**
* Handle form submission for the create action
*/
async store({}: HttpContext) {}

/**
* Show individual record
*/
async show({}: HttpContext) {}

/**
* Edit individual record
*/
async edit({}: HttpContext) {}

/**
* Handle form submission for the edit action
*/
async update({}: HttpContext) {}

/**
* Delete record
*/
async destroy({}: HttpContext) {}
}
35 changes: 35 additions & 0 deletions playgrounds/inertia-react/app/controllers/users_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import app from '@adonisjs/core/services/app'
import type { HttpContext } from '@adonisjs/core/http'

import { getUsersValidator, uploadFileValidator } from '../validators/main.js'

export default class UsersController {
async index({ response, request }: HttpContext) {
await request.validateUsing(getUsersValidator)

if (Math.random() > 0.5) {
return response.badGateway('Something went wrong' as const)
}

if (Math.random() > 0.5) {
return response.badRequest({ message: 'Invalid request' as const })
}

return {
users: [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Doe' },
],
}
}

async simpleText() {
return 'foo' as const
}

async fileUpload({ request }: HttpContext) {
const payload = await request.validateUsing(uploadFileValidator)
await payload.file.move(app.makePath('uploads'))
return { message: 'File uploaded' as const }
}
}
17 changes: 17 additions & 0 deletions playgrounds/inertia-react/config/tuyau.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { defineConfig } from '@tuyau/core'

const tuyauConfig = defineConfig({
codegen: {
/**
* Filters the definitions and named routes to be generated
*/
// definitions: {
// only: [],
// }
// routes: {
// only: [],
// }
}

Check warning on line 14 in playgrounds/inertia-react/config/tuyau.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `,`
})

export default tuyauConfig

Check warning on line 17 in playgrounds/inertia-react/config/tuyau.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `⏎`
Loading

0 comments on commit 7427ac6

Please sign in to comment.