diff --git a/.github/workflows/build_and_release.yml b/.github/workflows/build_and_release.yml deleted file mode 100644 index 962c818..0000000 --- a/.github/workflows/build_and_release.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Build client - -on: - release: - types: [created] - -jobs: - test_and_build: - name: Build client - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: oven-sh/setup-bun@v1 - - name: Install dependencies - run: bun i - - name: Run build - run: bun run build - env: - REF_NAME: ${{ github.ref_name }} - - name: Set tag - id: tag - run: | - if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then - echo "tag=dev" >> $GITHUB_OUTPUT - elif [[ "${{ github.event.release.prerelease }}" == "false" ]]; then - echo "tag=latest" >> $GITHUB_OUTPUT - fi - - uses: JS-DevTools/npm-publish@v3 - with: - token: ${{ secrets.NPM_TOKEN }} - package: ./dist/package.json - tag: ${{ steps.tag.outputs.tag }} \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..d814023 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,90 @@ +name: Publish new version + +on: + push: + branches: + - main + paths: + - package.json + workflow_dispatch: + +jobs: + tag: + name: Tag + runs-on: ubuntu-latest + permissions: + contents: write + outputs: + created: ${{ steps.version_tag.outputs.created }} + version_tag: ${{ steps.version_tag.outputs.version_tag }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Ensure version tag exists + id: version_tag + run: | + VERSION=$(bun -p "require('./package.json').version") + VERSION_TAG="v${VERSION}" + + echo "version_tag=${VERSION_TAG}" >> $GITHUB_OUTPUT + echo "created=false" >> $GITHUB_OUTPUT + + git fetch --tags + + if ! git rev-parse "${VERSION_TAG}" >/dev/null 2>&1; then + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag "${VERSION_TAG}" + git push origin "${VERSION_TAG}" + echo "created=true" >> $GITHUB_OUTPUT + echo "Created and pushed ${VERSION_TAG}." + fi + + release: + name: Release + runs-on: ubuntu-latest + needs: [tag] + if: needs.tag.outputs.created == 'true' + permissions: + contents: write + steps: + - name: Create release from new tag + uses: actions/github-script@v7 + with: + script: | + const tag = '${{ needs.tag.outputs.version_tag }}'; + const prerelease = tag.includes('-'); + + await github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + tag_name: tag, + name: tag, + prerelease, + generate_release_notes: true, + }); + + publish: + name: Publish + runs-on: ubuntu-latest + permissions: + id-token: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Install npm + run: bun add -g npm + - name: Install dependencies + run: NODE_ENV=development bun i + - name: Build and publish + run: bunx npm publish --provenance --access public diff --git a/LICENSE b/LICENSE index 75e12f4..121bb80 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Ade Yahya Prasetyo, 2024 m1212e +Copyright (c) 2021 Ade Yahya Prasetyo, 2024 m1212e, 2026 Aran Leite Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 48dd1a6..6c8e5ba 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,13 @@ -# prismabox +# @hyoretsu/prismabox Generate versatile [typebox](https://github.com/sinclairzx81/typebox) schemes from your [prisma](https://github.com/prisma) schema. > Currently does not support [mongoDB composite types](https://www.prisma.io/docs/orm/prisma-schema/data-model/models#defining-composite-types) -> Development is currently on hold, please see [here](https://github.com/m1212e/prismabox/issues/59) - Install it in your project, ```bash -npm i -D prismabox -pnpm i -D prismabox -bun add -D prismabox +npm i -D @hyoretsu/prismabox +pnpm i -D @hyoretsu/prismabox +bun a -D @hyoretsu/prismabox ``` then add @@ -22,6 +20,10 @@ generator prismabox { typeboxImportVariableName = "t" // you also can specify the dependency from which the above import should happen. This is useful if a package re-exports the typebox package and you would like to use that typeboxImportDependencyName = "elysia" + // optionally generate TS aliases for each schema: export type MySchema = UnwrapSchema + generateTsTypes = true + // optionally customize the unwrap utility type symbol (imported from the same package as typeboxImportDependencyName) + unwrapSchemaImportName = "UnwrapSchema" // by default the generated schemes do not allow additional properties. You can allow them by setting this to true additionalProperties = true // optionally enable the data model generation. See the data model section below for more info @@ -42,7 +44,7 @@ Prismabox offers annotations to adjust the output of models and fields. | @prismabox.create.input.hide | - | Hides the field or model from the outputs only in the input create model| | @prismabox.update.input.hide | - | Hides the field or model from the outputs only in the input update model| | @prismabox.options | @prismabox.options{ min: 10, max: 20 } | Uses the provided options for the field or model in the generated schema. Be careful to use valid JS/TS syntax! | -| @prismabox.typeOverwrite | @prismabox.typeOverwrite=Type.CustomName | Overwrite the type prismabox outputs for a field with a custom string. See [m1212e/prismabox#29](https://github.com/m1212e/prismabox/issues/29) for an extended usecase | +| @prismabox.typeOverwrite | @prismabox.typeOverwrite=Type.CustomName | Overwrite the type prismabox outputs for a field with a custom string. See [hyoretsu/prismabox#29](https://github.com/hyoretsu/prismabox/issues/29) for an extended usecase | > For a more detailed list of available annotations, please see [annotations.ts](src/annotations/annotations.ts) @@ -113,6 +115,6 @@ If enabled, the generator will additonally output more schemes for each model wh ## Notes ### `__nullable__` vs `Type.Optional` -Prismabox wraps nullable fields in a custom `__nullable__` method which allows `null` in addition to `undefined`. From the relevant [issue comment](https://github.com/m1212e/prismabox/issues/33#issuecomment-2708755442): +Prismabox wraps nullable fields in a custom `__nullable__` method which allows `null` in addition to `undefined`. From the relevant [issue comment](https://github.com/hyoretsu/prismabox/issues/33#issuecomment-2708755442): > prisma in some scenarios allows null OR undefined as types where optional only allows for undefined/is reflected as undefined in TS types diff --git a/build.ts b/build.ts index 4cefafd..4b1e628 100644 --- a/build.ts +++ b/build.ts @@ -30,14 +30,3 @@ let version = process.env.REF_NAME ?? packagejson.version; if (!version) { version = "0.0.1"; } - -await copyFile("./README.md", "./dist/README.md"); -await copyFile("./LICENSE", "./dist/LICENSE"); -await writeFile( - "./dist/package.json", - JSON.stringify({ - ...packagejson, - version, - bin: { prismabox: "cli.js" }, - }), -); diff --git a/bun.lockb b/bun.lockb index c6f002f..6ea0aee 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index a1d496e..2aa6f2c 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "name": "prismabox", - "version": "2.2.0", + "name": "@hyoretsu/prismabox", + "version": "1.2.2", "license": "MIT", "description": "Typebox generator for prisma schema", "author": { - "name": "Ade Yahya Prasetyo, m1212e" + "name": "Ade Yahya Prasetyo, m1212e, Aran Leite" }, "keywords": [ "prisma2", @@ -16,15 +16,22 @@ "typebox-generator", "prismabox" ], - "homepage": "https://github.com/m1212e/prismabox", + "homepage": "https://github.com/hyoretsu/prismabox", "repository": { - "url": "https://github.com/m1212e/prismabox.git" + "url": "git+https://github.com/hyoretsu/prismabox.git" }, + "bin": { + "prismabox": "dist/cli.js" + }, + "files": [ + "dist" + ], "scripts": { "dev": "bun run build && bunx prisma generate", "build": "bun run typecheck && bun build.ts", "lint": "biome check --write .", "format": "bun run lint", + "prepublishOnly": "bun run build", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/src/config.ts b/src/config.ts index 85bf5f9..975cdf7 100644 --- a/src/config.ts +++ b/src/config.ts @@ -15,6 +15,18 @@ const configSchema = Type.Object( * The name of the dependency to import the Type from typebox */ typeboxImportDependencyName: Type.String({ default: "@sinclair/typebox" }), + /** + * Whether to generate TypeScript type aliases for each generated schema. + * + * When enabled, every `export const Foo = ...` also emits + * `export type Foo = UnwrapSchema`. + */ + generateTsTypes: Type.Boolean({ default: true }), + /** + * The symbol name used to unwrap a schema into a TypeScript type. + * Imported as a type from `typeboxImportDependencyName`. + */ + unwrapSchemaImportName: Type.String({ default: "Static" }), /** * Whether to allow additional properties in the generated schemes */ @@ -49,7 +61,7 @@ const configSchema = Type.Object( allowRecursion: Type.Boolean({ default: true }), /** * Additional fields to add to the generated schemes (must be valid strings in the context of usage) - * @example + * @example * ```prisma * generator prismabox { provider = "node ./dist/cli.js" diff --git a/src/generators/wrappers/composite.ts b/src/generators/wrappers/composite.ts index 0254bc6..efbc46a 100644 --- a/src/generators/wrappers/composite.ts +++ b/src/generators/wrappers/composite.ts @@ -1,8 +1,19 @@ import { generateTypeboxOptions } from "../../annotations/options"; import { getConfig } from "../../config"; -export function makeComposite(inputModels: string[]) { - return `${ - getConfig().typeboxImportVariableName - }.Composite([${inputModels.map((i) => `${getConfig().exportedTypePrefix}${i}`).join(",")}], ${generateTypeboxOptions()})\n`; +function makePartial(input: string) { + return `${getConfig().typeboxImportVariableName}.Partial(${input})`; +} + +export function makeComposite(inputModels: string[], partial: boolean[] = []) { + const { exportedTypePrefix, typeboxImportVariableName } = getConfig(); + + return `${typeboxImportVariableName}.Composite([${inputModels + .map((model, i) => { + let modelStr = `${exportedTypePrefix}${model}`; + if (partial[i]) modelStr = makePartial(modelStr); + + return modelStr; + }) + .join(", ")}], ${generateTypeboxOptions()})\n`; } diff --git a/src/model.ts b/src/model.ts index 8258755..281f637 100644 --- a/src/model.ts +++ b/src/model.ts @@ -27,11 +27,25 @@ export type ProcessedModel = { function convertModelToStandalone( input: Pick, ) { - return `export const ${getConfig().exportedTypePrefix}${input.name} = ${input.stringRepresentation}\n`; + const generatedName = `${getConfig().exportedTypePrefix}${input.name}`; + + let exportStr = `export const ${generatedName} = ${input.stringRepresentation}\n`; + + if (getConfig().generateTsTypes) { + exportStr += `export type ${generatedName} = ${getConfig().unwrapSchemaImportName}\n`; + } + + return exportStr; } function typepoxImportStatement() { - return `import { ${getConfig().typeboxImportVariableName} } from "${ + let imports = getConfig().typeboxImportVariableName; + + if (getConfig().generateTsTypes) { + imports += `, type ${getConfig().unwrapSchemaImportName}`; + } + + return `import { ${imports} } from "${ getConfig().typeboxImportDependencyName }"\n`; } @@ -75,7 +89,10 @@ export function mapAllModelsForWrite() { const relations = processedRelations.find((e) => e.name === key); let composite: string; if (plain && relations) { - composite = makeComposite([`${key}Plain`, `${key}Relations`]); + composite = makeComposite( + [`${key}Plain`, `${key}Relations`], + [false, true], + ); } else if (plain) { composite = `${key}Plain`; } else if (relations) {