diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf2633118..769894b86 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,18 +21,22 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - - - name: Install pnpm - uses: pnpm/action-setup@v2 + - run: corepack enable - name: Set node version to 18 uses: actions/setup-node@v3 with: - node-version: 18.x + node-version: 18 + cache: "pnpm" + + - name: Install pnpm + uses: pnpm/action-setup@v2 - run: pnpm i - - run: pnpm run build:ci + - run: pnpm run build:stub + - run: pnpm run build:prepare + - run: pnpm run build - name: Run unit tests run: pnpm run test @@ -41,18 +45,19 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - - - name: Install pnpm - uses: pnpm/action-setup@v2 + - run: corepack enable - name: Set node version to 18 uses: actions/setup-node@v3 with: - node-version: 18.x + node-version: 18 + cache: "pnpm" - run: pnpm i - - run: pnpm run build:ci + - run: pnpm run build:stub + - run: pnpm run build:prepare + - run: pnpm run build - name: Run test coverage run: pnpm run test:coverage @@ -67,18 +72,19 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - - - name: Install pnpm - uses: pnpm/action-setup@v2 + - run: corepack enable - name: Set node version to 18 uses: actions/setup-node@v3 with: - node-version: 18.x + node-version: 18 + cache: "pnpm" - run: pnpm i - - run: pnpm run build:ci + - run: pnpm run build:stub + - run: pnpm run build:prepare + - run: pnpm run build - name: Run eslint run: pnpm run lint @@ -89,15 +95,16 @@ jobs: CI_JOB_NUMBER: 1 steps: - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - - - name: Install pnpm - uses: pnpm/action-setup@v2 + - run: corepack enable - name: Set node version to 18 uses: actions/setup-node@v3 with: - node-version: 18.x + node-version: 18 + cache: "pnpm" - run: pnpm i - - run: pnpm run build:ci + - run: pnpm run build:stub + - run: pnpm run build:prepare + - run: pnpm run build - run: pnpm run size diff --git a/docs/content/2.api/2.model/1.options/3.namespace.md b/docs/content/2.api/2.model/1.options/3.namespace.md new file mode 100644 index 000000000..ca2efffa0 --- /dev/null +++ b/docs/content/2.api/2.model/1.options/3.namespace.md @@ -0,0 +1,30 @@ +--- +title: 'entity' +description: 'Defines the entity name in the store' +--- + +# `namespace` + +Define a namespace if you have multiple equal entity names. +Resulting in "{namespace}/{entity}" + +## Usage + +````js +class User extends Model { + static entity = 'users' + + static namespace = 'orm' + + static fields () { + return { + userId: this.attr(null) + } + } +} +```` + +## Typescript Declarations +````ts +const namespace: string = '' +```` diff --git a/docs/content/2.api/5.configuration.md b/docs/content/2.api/5.configuration.md index 60a1dd879..86bd1fef1 100644 --- a/docs/content/2.api/5.configuration.md +++ b/docs/content/2.api/5.configuration.md @@ -8,11 +8,12 @@ icon: heroicons-outline:adjustments ## `model` -| Option | Default | Description | -|------------|:-------:|:--------------------------------------------------------| -| `withMeta` | `false` | Activates the `_meta` field to be saved for every model | -| `visible` | `[*]` | Sets default visible fields for every model | -| `hidden` | `[]` | Sets default hidden fields for every model | +| Option | Default | Description | +|-------------|:-------:|:--------------------------------------------------------| +| `withMeta` | `false` | Activates the `_meta` field to be saved for every model | +| `visible` | `[*]` | Sets default visible fields for every model | +| `hidden` | `[]` | Sets default hidden fields for every model | +| `namespace` | `''` | Define a namespace for the store as prefix | ## `cache` @@ -27,6 +28,7 @@ icon: heroicons-outline:adjustments export interface ModelConfigOptions { withMeta?: boolean hidden?: string[] + namespace?: string visible?: string[] } diff --git a/package.json b/package.json index 1777a2e24..e66f71ead 100644 --- a/package.json +++ b/package.json @@ -4,19 +4,20 @@ "packageManager": "pnpm@8.7.1", "scripts": { "release": "node scripts/release.mjs", - "size": "lerna run size --scope pinia-orm", - "build": "lerna run build --ignore @pina-orm/playground-*", + "cleanup": "rimraf 'packages/**/node_modules' 'playground/node_modules' 'node_modules'", + "size": "pnpm --filter './packages/**' size", + "build": "pnpm --filter './packages/**' build", + "build:stub": "pnpm --filter './packages/**' build --stub", + "build:prepare": "pnpm --filter './packages/**' dev:prepare", "sponsor": "sponsorkit", - "build:ci": "lerna run build --ignore @pina-orm/playground-* --ignore @pinia-orm/nuxt", "build:pinia-orm": "lerna run build --scope pinia-orm", "play": "lerna run play", - "build:dts": "lerna run build:dts --parallel", "format": "prettier -c --parser typescript \"packages/*/{src,__tests__,e2e}/**/*.[jt]s?(x)\"", "format:fix": "pnpm run format --write", - "lint": "lerna run lint --ignore @pina-orm/playground-* --ignore @pina-orm/playground-nuxt3", - "lint:fix": "lerna run lint:fix --ignore @pina-orm/playground-* --ignore @pina-orm/playground-nuxt3", - "test": "lerna run test --scope pinia-orm", - "test:coverage": "lerna run coverage --scope pinia-orm", + "lint": "pnpm --filter './packages/**' lint", + "lint:fix": "pnpm --filter './packages/**' lint:fix", + "test": "pnpm --filter './packages/**' test", + "test:coverage": "pnpm --filter './packages/**' coverage", "test:types": "tsc --build ./tsconfig.json", "test:dts": "lerna run test:dts", "docs:api": "lerna run docs:api --scope @pinia/docs" diff --git a/packages/nuxt/src/runtime/plugin.vue2.ts b/packages/nuxt/src/runtime/plugin.vue2.ts index 8acc455b2..fb65ef48c 100644 --- a/packages/nuxt/src/runtime/plugin.vue2.ts +++ b/packages/nuxt/src/runtime/plugin.vue2.ts @@ -2,6 +2,5 @@ import { createORM } from 'pinia-orm' import { ormOptions } from '#build/orm-options' export default function (ctx: any) { - // eslint-disable-next-line import/no-named-as-default-member ctx.$pinia.use(createORM(ormOptions)) } diff --git a/packages/nuxt/src/runtime/plugin.vue3.ts b/packages/nuxt/src/runtime/plugin.vue3.ts index 4b83243a2..269ab374b 100644 --- a/packages/nuxt/src/runtime/plugin.vue3.ts +++ b/packages/nuxt/src/runtime/plugin.vue3.ts @@ -4,7 +4,6 @@ import { defineNuxtPlugin } from '#app' import { ormOptions } from '#build/orm-options' export default defineNuxtPlugin((nuxtApp) => { - // eslint-disable-next-line import/no-named-as-default-member nuxtApp.$pinia.use(createORM(ormOptions)) setActivePinia(nuxtApp.$pinia) }) diff --git a/packages/pinia-orm/build.config.ts b/packages/pinia-orm/build.config.ts index ecd02f8b2..01b4e28ff 100644 --- a/packages/pinia-orm/build.config.ts +++ b/packages/pinia-orm/build.config.ts @@ -1,5 +1,5 @@ // build.config.ts -import fs from 'node:fs' +// import fs from 'node:fs' import { defineBuildConfig } from 'unbuild' export default defineBuildConfig({ @@ -21,7 +21,7 @@ export default defineBuildConfig({ externals: ['@/composables', 'nanoid', 'uuid', 'nanoid/async', 'nanoid/non-secure', 'pinia'], rollup: { emitCJS: true - }, + } // hooks: { // 'build:done': (ctx) => { // ctx.buildEntries.filter(entry => entry.path.includes('.cjs') && !entry.path.includes('shared')).forEach((entry) => { diff --git a/packages/pinia-orm/src/model/Model.ts b/packages/pinia-orm/src/model/Model.ts index 8caf9d022..71c284305 100644 --- a/packages/pinia-orm/src/model/Model.ts +++ b/packages/pinia-orm/src/model/Model.ts @@ -72,6 +72,12 @@ export class Model { */ static baseEntity: string + /** + * Define a namespace if you have multiple equal entity names. + * Resulting in "{namespace}/{entity}" + */ + static namespace: string + /** * The primary key for the model. */ @@ -601,6 +607,20 @@ export class Model { return this.$self().config } + /** + * Get the namespace. + */ + $namespace (): String { + return this.$self().namespace ?? config.model.namespace + } + + /** + * Get the store name. + */ + $storeName (): string { + return (this.$namespace() ? this.$namespace() + '/' : '') + this.$baseEntity() + } + /** * Get the base entity for this model. */ diff --git a/packages/pinia-orm/src/query/Query.ts b/packages/pinia-orm/src/query/Query.ts index 9f5349a17..6ce793fa4 100644 --- a/packages/pinia-orm/src/query/Query.ts +++ b/packages/pinia-orm/src/query/Query.ts @@ -161,7 +161,7 @@ export class Query { * Commit a store action and get the data */ protected commit (name: string, payload?: any) { - const store = useDataStore(this.model.$baseEntity(), this.model.$piniaOptions(), this)(this.pinia) + const store = useDataStore(this.model.$storeName(), this.model.$piniaOptions(), this)(this.pinia) if (name && typeof store[name] === 'function') { store[name](payload, false) } if (this.cache && ['get', 'all', 'insert', 'flush', 'delete', 'update', 'destroy'].includes(name)) { this.cache.clear() } diff --git a/packages/pinia-orm/src/repository/Repository.ts b/packages/pinia-orm/src/repository/Repository.ts index e44a031d8..3a44fe72a 100644 --- a/packages/pinia-orm/src/repository/Repository.ts +++ b/packages/pinia-orm/src/repository/Repository.ts @@ -116,7 +116,7 @@ export class Repository { * Returns the pinia store used with this model */ piniaStore () { - return useDataStore(this.model.$entity(), this.model.$piniaOptions(), this.query())(this.pinia) + return useDataStore(this.model.$storeName(), this.model.$piniaOptions(), this.query())(this.pinia) } /** diff --git a/packages/pinia-orm/src/store/Config.ts b/packages/pinia-orm/src/store/Config.ts index 3a6591a57..0f6aab730 100644 --- a/packages/pinia-orm/src/store/Config.ts +++ b/packages/pinia-orm/src/store/Config.ts @@ -3,14 +3,15 @@ import type { FilledInstallOptions } from './Store' export const CONFIG_DEFAULTS = { model: { + namespace: '', withMeta: false, hidden: ['_meta'], - visible: ['*'], + visible: ['*'] }, cache: { shared: true, provider: WeakCache - }, + } } export const config: FilledInstallOptions = { ...CONFIG_DEFAULTS } diff --git a/packages/pinia-orm/src/store/Store.ts b/packages/pinia-orm/src/store/Store.ts index c6fa91bbf..4d9b8819c 100644 --- a/packages/pinia-orm/src/store/Store.ts +++ b/packages/pinia-orm/src/store/Store.ts @@ -6,6 +6,7 @@ import { CONFIG_DEFAULTS, config } from './Config' export interface ModelConfigOptions { withMeta?: boolean hidden?: string[] + namespace?: string visible?: string[] } diff --git a/packages/pinia-orm/tests/feature/repository/retrieves_has.spec.ts b/packages/pinia-orm/tests/feature/repository/retrieves_has.spec.ts index 8ed4ced40..be82644db 100644 --- a/packages/pinia-orm/tests/feature/repository/retrieves_has.spec.ts +++ b/packages/pinia-orm/tests/feature/repository/retrieves_has.spec.ts @@ -172,11 +172,14 @@ describe('feature/repository/retrieves_has', () => { query.where('title', 'Title 03') }).get() + const users3 = userRepo.whereHas('post').get() + const expected = [ { id: 2, name: 'Jane Doe' } ] expect(users).toHaveLength(1) + expect(users3).toHaveLength(2) assertInstanceOf(users, User) assertModels(users, expected) assertModels(users2, [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Doe' }]) @@ -202,12 +205,15 @@ describe('feature/repository/retrieves_has', () => { query.where('title', 'Title 03') }).get() + const users2 = userRepo.whereDoesntHave('posts').get() + const expected = [ { id: 2, name: 'Jane Doe' }, { id: 3, name: 'Johnny Doe' } ] expect(users).toHaveLength(2) + expect(users2).toHaveLength(1) assertInstanceOf(users, User) assertModels(users, expected) }) diff --git a/packages/pinia-orm/tests/feature/repository/retrieves_has_or.spec.ts b/packages/pinia-orm/tests/feature/repository/retrieves_has_or.spec.ts index 5a6bdbf7b..574806318 100644 --- a/packages/pinia-orm/tests/feature/repository/retrieves_has_or.spec.ts +++ b/packages/pinia-orm/tests/feature/repository/retrieves_has_or.spec.ts @@ -102,12 +102,18 @@ describe('feature/repository/retrieves_has_or', () => { .where('name', 'Johnny Doe') .get() + const users2 = userRepo + .orWhereHas('posts') + .where('name', 'Johnny Doe') + .get() + const expected = [ { id: 2, name: 'Jane Doe' }, { id: 3, name: 'Johnny Doe' } ] expect(users).toHaveLength(2) + expect(users2).toHaveLength(3) assertInstanceOf(users, User) assertModels(users, expected) }) @@ -135,12 +141,18 @@ describe('feature/repository/retrieves_has_or', () => { .where('name', 'Johnny Doe') .get() + const users2 = userRepo + .orWhereDoesntHave('posts') + .where('name', 'Johnny Doe') + .get() + const expected = [ { id: 2, name: 'Jane Doe' }, { id: 3, name: 'Johnny Doe' } ] expect(users).toHaveLength(2) + expect(users2).toHaveLength(1) assertInstanceOf(users, User) assertModels(users, expected) }) diff --git a/packages/pinia-orm/tests/unit/PiniaORM.spec.ts b/packages/pinia-orm/tests/unit/PiniaORM.spec.ts index b66efc34a..ffee3bce4 100644 --- a/packages/pinia-orm/tests/unit/PiniaORM.spec.ts +++ b/packages/pinia-orm/tests/unit/PiniaORM.spec.ts @@ -45,15 +45,6 @@ describe('unit/PiniaORM', () => { }) it('pass config "model.visible"', () => { - Model.clearRegistries() - class User extends Model { - static entity = 'users' - - @Attr(0) declare id: number - @Str('') declare name: string - @Str('') declare username: string - } - createPiniaORM({ model: { visible: ['name'] } }) const userRepo = useRepo(User) @@ -68,15 +59,6 @@ describe('unit/PiniaORM', () => { }) it('pass config "cache false"', () => { - Model.clearRegistries() - class User extends Model { - static entity = 'users' - - @Attr(0) declare id: number - @Str('') declare name: string - @Str('') declare username: string - } - createPiniaORM({ cache: false }) const userRepo = useRepo(User) @@ -90,15 +72,6 @@ describe('unit/PiniaORM', () => { }) it('pass config "cache true"', () => { - Model.clearRegistries() - class User extends Model { - static entity = 'users' - - @Attr(0) declare id: number - @Str('') declare name: string - @Str('') declare username: string - } - createPiniaORM({ cache: true }) const userRepo = useRepo(User) @@ -112,15 +85,6 @@ describe('unit/PiniaORM', () => { }) it('pass config "cache.shared false"', () => { - Model.clearRegistries() - class User extends Model { - static entity = 'users' - - @Attr(0) declare id: number - @Str('') declare name: string - @Str('') declare username: string - } - createPiniaORM({ cache: { shared: false @@ -136,4 +100,39 @@ describe('unit/PiniaORM', () => { expect(userRepo.cache()).toBeInstanceOf(WeakCache) }) + + it('can a pass a namespace for each model', () => { + createPiniaORM({ model: { namespace: 'orm' } }) + + const userRepo = useRepo(User) + const user = userRepo.save({ + id: 1, + name: 'John', + username: 'JD' + }) + + expect(user.$storeName()).toBe('orm/users') + }) + + it('can overwrite namespace for a model', () => { + class User extends Model { + static entity = 'users' + + static namespace = 'otherOrm' + + @Attr(0) declare id: number + @Str('') declare name: string + @Str('') declare username: string + } + createPiniaORM({ model: { nameSpace: 'orm' } }) + + const userRepo = useRepo(User) + const user = userRepo.save({ + id: 1, + name: 'John', + username: 'JD' + }) + + expect(user.$storeName()).toBe('otherOrm/users') + }) }) diff --git a/packages/pinia-orm/tests/unit/model/Model_STI.spec.ts b/packages/pinia-orm/tests/unit/model/Model_STI.spec.ts index c81f4d107..533744258 100644 --- a/packages/pinia-orm/tests/unit/model/Model_STI.spec.ts +++ b/packages/pinia-orm/tests/unit/model/Model_STI.spec.ts @@ -305,7 +305,7 @@ describe('unit/model/Model_STI', () => { 2: { id: 2, type: 'ADULT', name: 'Jane Doe', job: 'Software Engineer' } } }) - const persons = useRepo(Person).all() + const persons = useRepo(Person).query().all() expect(persons[0]).toBeInstanceOf(Person) expect(persons[0]).not.toHaveProperty('type') expect(persons[1]).toBeInstanceOf(Adult)