Skip to content

Commit

Permalink
feat: add react-prisma (#4820)
Browse files Browse the repository at this point in the history
Co-authored-by: Joël Galeran <Jolg42@users.noreply.github.com>
  • Loading branch information
timsuchanek and Jolg42 committed Dec 30, 2020
1 parent 0e4f86c commit aeaa670
Show file tree
Hide file tree
Showing 15 changed files with 380 additions and 81 deletions.
Expand Up @@ -3,7 +3,7 @@
exports[`should work with a custom output dir 1`] = `
Prisma schema loaded from prisma/schema.prisma
✔ Generated Prisma Client (2.11.0-dev.20) to ./generated/client in XXms
✔ Generated Prisma Client (2.13.1) to ./generated/client in XXms
You can now start using Prisma Client in your code. Reference: https://pris.ly/d/client
\`\`\`
import { PrismaClient } from './generated/client'
Expand Down
Expand Up @@ -2,22 +2,22 @@

exports[`version basic version 1`] = `
@prisma/cli : 2.11.0-dev.20
@prisma/client : 2.11.0-dev.20
@prisma/client : 2.13.1
Current platform : TEST_PLATFORM
Query Engine : query-engine dfc9594ba25725d1eb319314b9cb6a229aa43573
Migration Engine : migration-engine-cli dfc9594ba25725d1eb319314b9cb6a229aa43573
Introspection Engine : introspection-core dfc9594ba25725d1eb319314b9cb6a229aa43573
Format Binary : prisma-fmt dfc9594ba25725d1eb319314b9cb6a229aa43573
Studio : 0.327.0
Query Engine : query-engine 59cf08bf47e90cb37532900059f3328b4328237c
Migration Engine : migration-engine-cli 59cf08bf47e90cb37532900059f3328b4328237c
Introspection Engine : introspection-core 59cf08bf47e90cb37532900059f3328b4328237c
Format Binary : prisma-fmt 59cf08bf47e90cb37532900059f3328b4328237c
Studio : 0.332.0
`;

exports[`version version with custom binaries 1`] = `
@prisma/cli : 2.11.0-dev.20
@prisma/client : 2.11.0-dev.20
@prisma/client : 2.13.1
Current platform : TEST_PLATFORM
Query Engine : query-engine 58369335532e47bdcec77a2f1e7c1fb83a463918
Migration Engine : migration-engine-cli 58369335532e47bdcec77a2f1e7c1fb83a463918
Introspection Engine : introspection-core 58369335532e47bdcec77a2f1e7c1fb83a463918
Format Binary : prisma-fmt 58369335532e47bdcec77a2f1e7c1fb83a463918
Studio : 0.327.0
Studio : 0.332.0
`;
2 changes: 1 addition & 1 deletion src/packages/client/jest.config.js
Expand Up @@ -23,6 +23,6 @@ module.exports = {
],
collectCoverageFrom: ['src/**/*.ts', '!**/__tests__/**/*'],
snapshotSerializers: ['./helpers/jestSnapshotSerializer'],
testTimeout: 70000,
testTimeout: 80000,
setupFiles: ['./helpers/jestSetup.js'],
}
2 changes: 1 addition & 1 deletion src/packages/client/src/__tests__/types/types.test.ts
Expand Up @@ -9,7 +9,7 @@ import tsd from 'tsd'
import formatter from 'tsd/dist/lib/formatter'
const del = promisify(rimraf)

jest.setTimeout(70000)
jest.setTimeout(80000)

let packageSource: string
beforeAll(async () => {
Expand Down
2 changes: 2 additions & 0 deletions src/packages/react-prisma/.eslintignore
@@ -0,0 +1,2 @@
node_modules
dist
6 changes: 6 additions & 0 deletions src/packages/react-prisma/.eslintrc.js
@@ -0,0 +1,6 @@
const path = require('path')
const config = require('../../../.eslintrc.js')

config.parserOptions.project.push(path.join(__dirname, 'tsconfig.json'))

module.exports = config
11 changes: 11 additions & 0 deletions src/packages/react-prisma/.gitignore
@@ -0,0 +1,11 @@
node_modules
.DS_Store
yarn-error.log
pnpm-debug.log

*.db

dist/

*.tsbuildinfo
sandbox
1 change: 1 addition & 0 deletions src/packages/react-prisma/.prettierignore
@@ -0,0 +1 @@
dist
5 changes: 5 additions & 0 deletions src/packages/react-prisma/.prettierrc.yml
@@ -0,0 +1,5 @@
tabWidth: 2
trailingComma: all
singleQuote: true
semi: false
printWidth: 80
21 changes: 21 additions & 0 deletions src/packages/react-prisma/README.md
@@ -0,0 +1,21 @@
# `react-prisma`

This package allows using the Prisma Client in a React Server Component.
It is a thin wrapper arround `@prisma/client`.

**This is highly experimental. Don't use this in any real application**

We just publish this early for demonstration purposes.

# This is unstable

We don't guarantee that this works. Any moment the underlying `react` api can change and break this package.

# Known limitations

Right now this package does not support query chaining.
You can for example not do this yet:

```ts
prisma.user.findUnique({ where: { id: 42 } }).posts()
```
9 changes: 9 additions & 0 deletions src/packages/react-prisma/jest.config.js
@@ -0,0 +1,9 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
collectCoverage: process.env.CI ? true : false,
coverageReporters: ['clover'],
coverageDirectory: 'src/__tests__/coverage',
collectCoverageFrom: ['src/**/*.ts', '!**/__tests__/**/*'],
testMatch: ['**/src/__tests__/**/*.test.ts'],
}
51 changes: 51 additions & 0 deletions src/packages/react-prisma/package.json
@@ -0,0 +1,51 @@
{
"name": "react-prisma",
"version": "0.0.5",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "Apache-2.0",
"author": "Tim Suchanek <suchanek@prisma.io>",
"repository": "git@github.com:prisma/prisma.git",
"devDependencies": {
"@types/jest": "26.0.19",
"@types/node": "12.19.11",
"@typescript-eslint/eslint-plugin": "4.10.0",
"@typescript-eslint/parser": "4.10.0",
"eslint": "7.15.0",
"eslint-config-prettier": "7.0.0",
"eslint-plugin-eslint-comments": "3.2.0",
"eslint-plugin-jest": "24.1.3",
"eslint-plugin-prettier": "3.3.0",
"jest": "26.6.3",
"lint-staged": "10.5.3",
"prettier": "2.2.1",
"strip-ansi": "6.0.0",
"ts-jest": "26.4.4",
"typescript": "4.1.3",
"react": "^17.0.0",
"@prisma/client": ">=2.0.0"
},
"scripts": {
"build": "tsc -d",
"prepublishOnly": "pnpm run build",
"format": "prettier --write .",
"lint": "eslint --cache --fix --ext .ts .",
"lint-ci": "eslint --ext .ts .",
"test": "echo \"not yet\"",
"precommit": "lint-staged"
},
"files": [
"!**/__tests__",
"dist"
],
"lint-staged": {
"*.ts": [
"eslint",
"prettier --write"
]
},
"peerDependencies": {
"react": "^17.0.0",
"@prisma/client": "*"
}
}
116 changes: 116 additions & 0 deletions src/packages/react-prisma/src/index.ts
@@ -0,0 +1,116 @@
import { unstable_getCacheForType, Wakeable } from 'react'
import { PrismaClient as PrismaClientConstructor, dmmf } from '@prisma/client'

const Pending = 0
const Resolved = 1
const Rejected = 2

type Pending = 0
type Resolved = 1
type Rejected = 2

type PendingRecord = {
status: Pending
value: Wakeable
}

type ResolvedRecord = {
status: Resolved
value: any
}

type RejectedRecord = {
status: Rejected
value: any
}

type Record = PendingRecord | ResolvedRecord | RejectedRecord

function createRecordFromThenable(thenable): Record {
const record: Record = {
status: Pending,
value: thenable,
}
thenable.then(
(value) => {
if (record.status === Pending) {
const resolvedRecord: any = record
resolvedRecord.status = Resolved
resolvedRecord.value = value
}
},
(err) => {
if (record.status === Pending) {
const rejectedRecord: any = record
rejectedRecord.status = Rejected
rejectedRecord.value = err
}
},
)
return record
}

function readRecordValue(record) {
if (record.status === Resolved) {
return record.value
} else {
throw record.value
}
}

function lowercase(str) {
return str.slice(0, 1).toLowerCase() + str.slice(1)
}

const queryOperations = {
findMany: true,
findFirst: true,
findOne: true,
findUnique: true,
}

export function PrismaClient(this, options): PrismaClientConstructor {
this.client = new PrismaClientConstructor(options)
// Unique function per instance because it's used for cache identity.
this.createRecordMap = function () {
return new Map()
}

for (let i = 0; i < dmmf.mappings.modelOperations.length; i++) {
const mapping = dmmf.mappings.modelOperations[i]
const delegate = Object.create(null)
const modelName = lowercase(mapping.model)

const keys = Object.keys(this.client[modelName])
for (let i = 0; i < keys.length; i++) {
const method = keys[i]
delegate[method] = (query) => {
if (!queryOperations[method]) {
throw new Error(`The mutation ${modelName}.${method} can't be used from \`react-prisma\`.
Please use \`@prisma/client\` directly for that.`)
}
const outerMap = unstable_getCacheForType(this.createRecordMap)

const innerMap = outerMap
const key = query

let record = innerMap.get(key)
if (!record) {
const thenable = this.client[modelName][method](query)
record = createRecordFromThenable(thenable)
innerMap.set(key, record)
} else if (record instanceof Map) {
throw new Error(
'This query has received fewer parameters than the last time ' +
'the same query was used. Always pass the exact number of ' +
'parameters that the query needs.',
)
}
const result = readRecordValue(record)
return result
}
}

this[modelName] = delegate
}
}
16 changes: 16 additions & 0 deletions src/packages/react-prisma/tsconfig.json
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"lib": ["esnext"],
"module": "commonjs",
"target": "es2018",
"strict": true,
"esModuleInterop": true,
"sourceMap": true,
"noImplicitAny": false,
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"incremental": true
},
"exclude": ["dist", "build", "scripts", "sandbox"]
}

0 comments on commit aeaa670

Please sign in to comment.