Skip to content

Commit

Permalink
feat(generators): Add service file for shared information (#3008)
Browse files Browse the repository at this point in the history
  • Loading branch information
daffl authored Jan 29, 2023
1 parent 00cb0d9 commit 0a1665d
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 71 deletions.
4 changes: 4 additions & 0 deletions docs/.vitepress/config.sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ export default {
{
text: '📄 <service>.schemas',
link: '/guides/cli/service.schemas.md'
},
{
text: '📄 <service>.shared',
link: '/guides/cli/service.shared.md'
}
]
},
Expand Down
18 changes: 18 additions & 0 deletions docs/guides/cli/service.shared.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
outline: deep
---

# Service Shared

The `<service>.shared` file contains variables and type declarations that are shared between the [client](./client.md) and the [server application](./app.md). It can also be used for shared utility functions or schemas (e.g. for client side validation).

## Variables

By default two shared variables are exported:

- `<name>Path` - The path of the service. Changing this will change the path for the service in all places like the application, the client and types
- `<name>Methods` - The list of service methods available to the client. This can be updated with service and custom methods a client should be able to use.

## Client setup

This file also includes the client side service registration which will be included in the [client](./client.md). It will register a client side service based on the shared path and methods.
2 changes: 1 addition & 1 deletion packages/generators/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"scripts": {
"prepublish": "npm run compile",
"compile": "shx rm -rf lib/ && tsc && shx cp -r src/. lib/",
"test": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts"
"test": "npm run compile && mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts"
},
"directories": {
"lib": "lib"
Expand Down
13 changes: 9 additions & 4 deletions packages/generators/src/app/templates/client.tpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ const template = ({
language
}: AppGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/client.html
import { feathers } from '@feathersjs/feathers'
import type { TransportConnection, Params } from '@feathersjs/feathers'
import type { TransportConnection, Application } from '@feathersjs/feathers'
import authenticationClient from '@feathersjs/authentication-client'
import type { AuthenticationClientOptions } from '@feathersjs/authentication-client'
export interface ServiceTypes {
//
export interface Configuration {
connection: TransportConnection<ServiceTypes>
}
export interface ServiceTypes {}
export type ClientApplication = Application<ServiceTypes, Configuration>
/**
* Returns a ${language === 'ts' ? 'typed' : ''} client for the ${name} app.
*
Expand All @@ -27,10 +31,11 @@ export const createClient = <Configuration = any> (
connection: TransportConnection<ServiceTypes>,
authenticationOptions: Partial<AuthenticationClientOptions> = {}
) => {
const client = feathers<ServiceTypes, Configuration>()
const client: ClientApplication = feathers()
client.configure(connection)
client.configure(authenticationClient(authenticationOptions))
client.set('connection', connection)
return client
}
Expand Down
2 changes: 1 addition & 1 deletion packages/generators/src/app/templates/package.json.tpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const packageJson = ({
lib,
test
},
files: ['lib/client.js', 'lib/**/*.d.ts'],
files: ['lib/client.js', 'lib/**/*.d.ts', 'lib/**/*.shared.js'],
main: language === 'ts' ? 'lib/client' : `${lib}/client`,
...(language === 'ts' ? tsPackageJson(lib) : jsPackageJson(lib))
})
Expand Down
53 changes: 14 additions & 39 deletions packages/generators/src/service/templates/client.tpl.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,31 @@
import { generator, toFile, when, after, before } from '@feathershq/pinion'
import { generator, toFile, after, before } from '@feathershq/pinion'
import { injectSource } from '../../commons'
import { ServiceGeneratorContext } from '../index'

const importTemplate = ({
upperName,
folder,
fileName,
className,
camelName,
type
}: ServiceGeneratorContext) => /* ts */ `import type {
${upperName},
${upperName}Data,
${upperName}Query,
${className}
} from './services/${folder.join('/')}/${fileName}'
const importTemplate = ({ upperName, folder, fileName, camelName }: ServiceGeneratorContext) => /* ts */ `
import { ${camelName}Client } from './services/${folder.join('/')}/${fileName}.shared'
export type {
${upperName},
${upperName}Data,
${upperName}Query
}
export const ${camelName}ServiceMethods = ['find', 'get', 'create', 'patch', 'remove'] as const
export type ${upperName}ClientService = Pick<${className}${
type !== 'custom' ? `<Params<${upperName}Query>>` : ''
}, typeof ${camelName}ServiceMethods[number]>
${upperName}Query,
${upperName}Patch
} from './services/${folder.join('/')}/${fileName}.shared'
`

const declarationTemplate = ({ path, upperName }: ServiceGeneratorContext) =>
` '${path}': ${upperName}ClientService`

const registrationTemplate = ({
camelName,
path
}: ServiceGeneratorContext) => ` client.use('${path}', connection.service('${path}'), {
methods: ${camelName}ServiceMethods
})`
const registrationTemplate = ({ camelName }: ServiceGeneratorContext) =>
` client.configure(${camelName}Client)`

const toClientFile = toFile<ServiceGeneratorContext>(({ lib }) => [lib, 'client'])

export const generate = async (ctx: ServiceGeneratorContext) =>
generator(ctx)
.then(injectSource(registrationTemplate, before('return client'), toClientFile))
.then(
when(
(ctx) => ctx.language === 'js',
injectSource(importTemplate, after('import authenticationClient'), toClientFile)
)
)
.then(
when(
(ctx) => ctx.language === 'ts',
injectSource(importTemplate, after('import type { AuthenticationClientOptions }'), toClientFile),
injectSource(declarationTemplate, after('export interface ServiceTypes'), toClientFile)
injectSource(
importTemplate,
after<ServiceGeneratorContext>(({ language }) =>
language === 'ts' ? 'import type { AuthenticationClientOptions }' : 'import authenticationClient'
),
toClientFile
)
)
10 changes: 5 additions & 5 deletions packages/generators/src/service/templates/service.tpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export const template = ({
camelName,
authentication,
isEntityService,
path,
className,
relative,
schema,
Expand Down Expand Up @@ -34,21 +33,22 @@ import {
import type { Application } from '${relative}/declarations'
import { ${className}, getOptions } from './${fileName}.class'
import { ${camelName}Path, ${camelName}Methods } from './${fileName}.shared'
export * from './${fileName}.class'
${schema ? `export * from './${fileName}.schema'` : ''}
// A configure function that registers the service and its hooks via \`app.configure\`
export const ${camelName} = (app: Application) => {
// Register our service on the Feathers application
app.use('${path}', new ${className}(getOptions(app)), {
app.use(${camelName}Path, new ${className}(getOptions(app)), {
// A list of all methods this service exposes externally
methods: ['find', 'get', 'create', 'patch', 'remove'],
methods: ${camelName}Methods,
// You can add additional custom events to be sent to clients here
events: []
})
// Initialize hooks
app.service('${path}').hooks({
app.service(${camelName}Path).hooks({
around: {
all: [${
authentication
Expand Down Expand Up @@ -115,7 +115,7 @@ export const ${camelName} = (app: Application) => {
// Add this service to the service type index
declare module '${relative}/declarations' {
interface ServiceTypes {
'${path}': ${className}
[${camelName}Path]: ${className}
}
}
`
Expand Down
61 changes: 61 additions & 0 deletions packages/generators/src/service/templates/shared.tpl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { generator, toFile } from '@feathershq/pinion'
import { renderSource } from '../../commons'
import { ServiceGeneratorContext } from '../index'

const sharedTemplate = ({
camelName,
upperName,
className,
fileName,
relative,
path
}: ServiceGeneratorContext) => /* ts */ `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.shared.html
import type { Params } from '@feathersjs/feathers'
import type { ClientApplication } from '${relative}/client'
import type {
${upperName},
${upperName}Data,
${upperName}Patch,
${upperName}Query,
${className}
} from './${fileName}.class'
export type { ${upperName}, ${upperName}Data, ${upperName}Patch, ${upperName}Query }
export type ${upperName}ClientService = Pick<
${className}<Params<${upperName}Query>>,
typeof ${camelName}Methods[number]
>
export const ${camelName}Path = '${path}'
export const ${camelName}Methods = ['find', 'get', 'create', 'patch', 'remove'] as const
export const ${camelName}Client = (client: ClientApplication) => {
const connection = client.get('connection')
client.use(${camelName}Path, connection.service(${camelName}Path), {
methods: ${camelName}Methods
})
}
// Add this service to the client service type index
declare module '${relative}/client' {
interface ServiceTypes {
[${camelName}Path]: ${upperName}ClientService
}
}
`

export const generate = async (ctx: ServiceGeneratorContext) =>
generator(ctx).then(
renderSource(
sharedTemplate,
toFile(({ lib, folder, fileName }: ServiceGeneratorContext) => [
lib,
'services',
...folder,
`${fileName}.shared`
])
)
)
29 changes: 16 additions & 13 deletions packages/generators/src/service/type/custom.tpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ ${
} from './${fileName}.schema'
`
: `
export type ${upperName} = any
export type ${upperName}Data = any
export type ${upperName}Patch = any
export type ${upperName}Query = any
type ${upperName} = any
type ${upperName}Data = any
type ${upperName}Patch = any
type ${upperName}Query = any
`
}
export type { ${upperName}, ${upperName}Data, ${upperName}Patch, ${upperName}Query }
export interface ${className}Options {
app: Application
}
Expand All @@ -38,24 +40,25 @@ export interface ${upperName}Params extends Params<${upperName}Query> {
}
// This is a skeleton for a custom service class. Remove or add the methods you need here
export class ${className} implements ServiceInterface<${upperName}, ${upperName}Data, ${upperName}Params, ${upperName}Patch> {
export class ${className}<ServiceParams extends Params = ${upperName}Params>
implements ServiceInterface<${upperName}, ${upperName}Data, ServiceParams, ${upperName}Patch> {
constructor (public options: ${className}Options) {
}
async find (_params?: ${upperName}Params): Promise<${upperName}[]> {
async find (_params?: ServiceParams): Promise<${upperName}[]> {
return []
}
async get (id: Id, _params?: ${upperName}Params): Promise<${upperName}> {
async get (id: Id, _params?: ServiceParams): Promise<${upperName}> {
return {
id: 0,
text: \`A new message with ID: \${id}!\`
}
}
async create (data: ${upperName}Data, params?: ${upperName}Params): Promise<${upperName}>
async create (data: ${upperName}Data[], params?: ${upperName}Params): Promise<${upperName}[]>
async create (data: ${upperName}Data|${upperName}Data[], params?: ${upperName}Params): Promise<${upperName}|${upperName}[]> {
async create (data: ${upperName}Data, params?: ServiceParams): Promise<${upperName}>
async create (data: ${upperName}Data[], params?: ServiceParams): Promise<${upperName}[]>
async create (data: ${upperName}Data|${upperName}Data[], params?: ServiceParams): Promise<${upperName}|${upperName}[]> {
if (Array.isArray(data)) {
return Promise.all(data.map(current => this.create(current, params)));
}
Expand All @@ -67,22 +70,22 @@ export class ${className} implements ServiceInterface<${upperName}, ${upperName}
}
// This method has to be added to the 'methods' option to make it available to clients
async update (id: NullableId, data: ${upperName}Data, _params?: ${upperName}Params): Promise<${upperName}> {
async update (id: NullableId, data: ${upperName}Data, _params?: ServiceParams): Promise<${upperName}> {
return {
id: 0,
...data
}
}
async patch (id: NullableId, data: ${upperName}Patch, _params?: ${upperName}Params): Promise<${upperName}> {
async patch (id: NullableId, data: ${upperName}Patch, _params?: ServiceParams): Promise<${upperName}> {
return {
id: 0,
text: \`Fallback for \${id}\`,
...data
}
}
async remove (id: NullableId, _params?: ${upperName}Params): Promise<${upperName}> {
async remove (id: NullableId, _params?: ServiceParams): Promise<${upperName}> {
return {
id: 0,
text: 'removed'
Expand Down
10 changes: 6 additions & 4 deletions packages/generators/src/service/type/knex.tpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ ${
} from './${fileName}.schema'
`
: `
export type ${upperName} = any
export type ${upperName}Data = any
export type ${upperName}Patch = any
export type ${upperName}Query = any
type ${upperName} = any
type ${upperName}Data = any
type ${upperName}Patch = any
type ${upperName}Query = any
`
}
export type { ${upperName}, ${upperName}Data, ${upperName}Patch, ${upperName}Query }
export interface ${upperName}Params extends KnexAdapterParams<${upperName}Query> {
}
Expand Down
10 changes: 6 additions & 4 deletions packages/generators/src/service/type/mongodb.tpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ ${
} from './${fileName}.schema'
`
: `
export type ${upperName} = any
export type ${upperName}Data = any
export type ${upperName}Patch = any
export type ${upperName}Query = any
type ${upperName} = any
type ${upperName}Data = any
type ${upperName}Patch = any
type ${upperName}Query = any
`
}
export type { ${upperName}, ${upperName}Data, ${upperName}Patch, ${upperName}Query }
export interface ${upperName}Params extends MongoDBAdapterParams<${upperName}Query> {
}
Expand Down

0 comments on commit 0a1665d

Please sign in to comment.