diff --git a/package-lock.json b/package-lock.json index 73c1508d1e..3590d8306f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -786,56 +786,6 @@ "url": "https://github.com/sponsors/daffl" } }, - "node_modules/@feathersjs/cli": { - "version": "5.0.37", - "resolved": "https://registry.npmjs.org/@feathersjs/cli/-/cli-5.0.37.tgz", - "integrity": "sha512-NH/KWTzQtuqZZzy+JgrOUISh9Ep3JNshj8WgA2ExGvhfzKV3KuMenGg1hKhEDn5YUBEuH2e8O+DGe3Cg3bPzeg==", - "license": "MIT", - "dependencies": { - "@feathersjs/generators": "^5.0.37", - "chalk": "^5.6.2", - "commander": "^13.1.0" - }, - "bin": { - "feathers": "bin/feathers.js" - }, - "engines": { - "node": ">= 14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/daffl" - } - }, - "node_modules/@feathersjs/cli/node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@feathersjs/generators": { - "version": "5.0.37", - "resolved": "https://registry.npmjs.org/@feathersjs/generators/-/generators-5.0.37.tgz", - "integrity": "sha512-0cEWAxDYlf32Qb+7zIGSRXPz/uC+NNxDKucrOs9uLvDjL+mE16r6YyNC5yk0lgTg5L0fbvkoweSpMcUd+EBDqQ==", - "license": "MIT", - "dependencies": { - "@featherscloud/pinion": "^0.5.5", - "chalk": "^5.6.2", - "lodash": "^4.17.21", - "prettier": "^3.6.2", - "typescript": "^5.9.3" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/daffl" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -4955,15 +4905,6 @@ "dev": true, "license": "MIT" }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/execa": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", @@ -7032,6 +6973,7 @@ "version": "4.17.23", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, "license": "MIT" }, "node_modules/lodash.ismatch": { @@ -8859,6 +8801,7 @@ "version": "3.8.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -10714,6 +10657,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -11445,12 +11389,18 @@ "version": "6.0.0-pre.7", "license": "MIT", "dependencies": { - "@feathersjs/cli": "^5.0.34" + "@featherscloud/pinion": "^0.5.5", + "commander": "^12.1.0", + "type-fest": "^0.21.3" }, "bin": { "create-feathers": "bin/create-feathers.js" }, "devDependencies": { + "@types/node": "^24.1.0", + "@vitest/coverage-v8": "^3.2.4", + "shx": "^0.4.0", + "typescript": "^5.8.0", "vitest": "^3.2.4" }, "engines": { @@ -11461,12 +11411,19 @@ "url": "https://github.com/sponsors/daffl" } }, + "packages/create-feathers/node_modules/@types/node": { + "version": "24.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", + "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, "packages/feathers": { "version": "6.0.0-pre.7", "license": "MIT", - "dependencies": { - "events": "^3.3.0" - }, "devDependencies": { "@types/node": "^24.1.0", "@types/qs": "^6.14.0", @@ -11498,7 +11455,7 @@ "name": "feathersjs.com", "extraneous": true, "dependencies": { - "@feathersdev/websites": "^0.0.4", + "@feathersdev/websites": "^0.0.3", "@formkit/auto-animate": "^0.9.0", "@iconify-json/material-symbols": "^1.2.53", "@iconify-json/mdi": "^1.2.3", diff --git a/packages/create-feathers/bin/create-feathers.js b/packages/create-feathers/bin/create-feathers.js index 1c1bd6b108..6073a33b99 100755 --- a/packages/create-feathers/bin/create-feathers.js +++ b/packages/create-feathers/bin/create-feathers.js @@ -1,20 +1,28 @@ #!/usr/bin/env node -'use strict'; +'use strict' -import path from 'path' -import { existsSync } from 'fs' -import { mkdir } from 'fs/promises' -import { Command, commandRunner, chalk } from '@feathersjs/cli' +import path from 'node:path' +import { existsSync } from 'node:fs' +import { mkdir } from 'node:fs/promises' +import { Command } from 'commander' +import { generate, getContext } from '../lib/index.js' + +const color = { + green: (text) => `\x1b[32m${text}\x1b[0m`, + red: (text) => `\x1b[31m${text}\x1b[0m`, + grey: (text) => `\x1b[90m${text}\x1b[0m` +} const program = new Command() -const generateApp = commandRunner('app') program - .name('npm init feathers') - .description(`Create a new Feathers application 🕊️ + .name('npm create feathers') + .description( + `Create a new Feathers application 🕊️ -${chalk.grey('npm init feathers myapp')} -`) +${color.grey('npm create feathers myapp')} +` + ) .argument('', 'The name of your new application') // .version(version) .showHelpAfterError() @@ -28,21 +36,23 @@ ${chalk.grey('npm init feathers myapp')} await mkdir(cwd) - await generateApp({ + const ctx = getContext({ name, cwd, ...options }) + await generate(ctx) + console.log(` -${chalk.green('Hooray')}! Your Feathers app is ready to go! 🚀 -Go to the ${chalk.grey(name)} folder to get started. +${color.green('Hooray')}! Your Feathers app is ready to go! 🚀 +Go to the ${color.grey(name)} folder to get started. -To learn more visit ${chalk.grey('https://feathersjs.com/guides')} +To learn more visit ${color.grey('https://feathersjs.com')} `) } catch (error) { - console.error(`${chalk.red('Error')}: ${error.message}`) + console.error(`${color.red('Error')}: ${error.message}`) process.exit(1) } }) diff --git a/packages/create-feathers/package.json b/packages/create-feathers/package.json index da19224b55..d5960ee98a 100644 --- a/packages/create-feathers/package.json +++ b/packages/create-feathers/package.json @@ -42,16 +42,24 @@ "*.js" ], "scripts": { - "test": "bin/create-feathers.js --help" + "test": "bin/create-feathers.js --help", + "prepublish": "npm run compile", + "compile": "shx rm -rf lib/ && tsc" }, "publishConfig": { "access": "public" }, - "dependencies": { - "@feathersjs/cli": "^5.0.34" - }, "gitHead": "90caf635aec850550b9d37bea2762af959d9e8d5", "devDependencies": { + "@types/node": "^24.1.0", + "@vitest/coverage-v8": "^3.2.4", + "shx": "^0.4.0", + "typescript": "^5.8.0", "vitest": "^3.2.4" + }, + "dependencies": { + "@featherscloud/pinion": "^0.5.5", + "commander": "^12.1.0", + "type-fest": "^0.21.3" } } diff --git a/packages/create-feathers/src/commons.ts b/packages/create-feathers/src/commons.ts new file mode 100644 index 0000000000..e2d97058d5 --- /dev/null +++ b/packages/create-feathers/src/commons.ts @@ -0,0 +1,115 @@ +import fs from 'node:fs' +import { join, dirname } from 'node:path' +import { fileURLToPath } from 'node:url' +import { PackageJson } from 'type-fest' +import { Callable, PinionContext, loadJSON, fromFile, getCallable, exec } from '@featherscloud/pinion' + +// Set __dirname in es module +const __dirname = dirname(fileURLToPath(import.meta.url)) + +export const { version } = JSON.parse(fs.readFileSync(join(__dirname, '..', 'package.json')).toString()) + +export type DependencyVersions = { [key: string]: string } + +export type FeathersAppInfo = { + packager: 'yarn' | 'npm' | 'pnpm' + + platform: 'node' | 'deno' | 'bun' + + sse: boolean +} + +export interface AppPackageJson extends PackageJson { + feathers?: FeathersAppInfo +} + +export interface FeathersBaseContext extends PinionContext { + /** + * Information about the Feathers application (like chosen language, database etc.) + * usually taken from `package.json` + */ + feathers: FeathersAppInfo + /** + * The package.json file + */ + pkg: AppPackageJson + /** + * The folder where source files are put + */ + lib: string + /** + * The folder where test files are put + */ + test: string + /** + * A list dependencies that should be installed with a certain version. + * Used for installing development dependencies during testing. + */ + dependencyVersions?: DependencyVersions +} + +export interface AppGeneratorData extends FeathersAppInfo { + /** + * The application name + */ + name: string + /** + * A short description of the app + */ + description: string +} + +export type AppGeneratorContext = FeathersBaseContext & AppGeneratorData + +export type AppGeneratorArguments = FeathersBaseContext & Partial + +/** + * Loads the application package.json and populates information like the library and test directory + * and Feathers app specific information. + * + * @returns The updated context + */ +export const initializeBaseContext = + () => + (ctx: C) => + Promise.resolve(ctx).then(loadJSON(fromFile('package.json'), (pkg) => ({ pkg }), {})) + +/** + * A special error that can be thrown by generators. It contains additional + * information about the error that can be used to display a more helpful + * error message to the user. + */ +export class FeathersGeneratorError extends Error { + /** + * Additional information about the error. This can include things like + * the reason for the error and suggested actions to take. + */ + context?: Record + + /** + * Creates a new FeathersGeneratorError + * @param message The error message + * @param context Additional information about the error + */ + constructor(message: string, context?: Record) { + super(message) + this.name = 'FeathersGeneratorError' + this.context = context + } +} + +export const install = + ( + dependencies: Callable, + dev: Callable, + packager: Callable + ) => + async (ctx: C) => { + const dependencyList = await getCallable(dependencies, ctx) + const packageManager = await getCallable(packager, ctx) + const isDev = await getCallable(dev, ctx) + const flag = isDev ? (packageManager === 'yarn' ? ' --dev' : ' --save-dev') : '' + const command = packageManager === 'yarn' ? 'add' : 'install' + + return exec(`${packageManager} ${command} ${dependencyList.join(' ')}${flag}`, [])(ctx) + } diff --git a/packages/create-feathers/src/index.ts b/packages/create-feathers/src/index.ts new file mode 100644 index 0000000000..7cea4f34e6 --- /dev/null +++ b/packages/create-feathers/src/index.ts @@ -0,0 +1,88 @@ +import { sep, dirname } from 'path' +import { prompt, runGenerators } from '@featherscloud/pinion' +import { fileURLToPath } from 'url' + +import type { AppGeneratorArguments } from './commons.js' +import { initializeBaseContext, install } from './commons.js' + +export { getContext } from '@featherscloud/pinion' + +// Set __dirname in es module +const __dirname = dirname(fileURLToPath(import.meta.url)) + +export const generate = (ctx: AppGeneratorArguments) => + Promise.resolve(ctx) + .then(initializeBaseContext()) + .then((ctx) => ({ + ...ctx, + dependencies: [] as string[], + devDependencies: [] as string[] + })) + .then( + prompt((ctx) => [ + { + name: 'name', + type: 'input', + when: !ctx.name, + message: 'What is the name of your application?', + default: ctx.cwd.split(sep).pop(), + validate: (input) => { + if (ctx.dependencyVersions[input]) { + return `Application can not have the same name as a dependency` + } + + return true + } + }, + { + name: 'description', + type: 'input', + when: !ctx.description, + message: 'Write a short description' + }, + { + name: 'platform', + type: 'list', + when: !ctx.platform, + message: 'Which platform do you want to use?', + choices: [ + { value: 'node', name: 'Node' }, + { value: 'deno', name: 'Deno' }, + { value: 'bun', name: 'Bun' } + ] + }, + { + name: 'packager', + type: 'list', + when: ({ platform }) => platform === 'node' && !ctx.packager, + message: 'Which package manager are you using?', + choices: [ + { value: 'npm', name: 'npm' }, + { value: 'yarn', name: 'Yarn' }, + { value: 'pnpm', name: 'pnpm' } + ] + }, + { + name: 'sse', + type: 'confirm', + when: ctx.sse === undefined, + message: 'Enable real-time with Server-Sent Events (SSE)', + default: true + } + ]) + ) + .then(runGenerators(__dirname, 'templates')) + .then(initializeBaseContext()) + .then( + install(['feathers@pre'], false, (ctx): string => { + if (ctx.packager) { + return ctx.packager + } + + if (ctx.platform === 'deno' || ctx.platform === 'bun') { + return ctx.platform + } + + return 'npm' + }) + ) diff --git a/packages/create-feathers/src/templates/app.tpl.ts b/packages/create-feathers/src/templates/app.tpl.ts new file mode 100644 index 0000000000..dba1b47481 --- /dev/null +++ b/packages/create-feathers/src/templates/app.tpl.ts @@ -0,0 +1,37 @@ +import { renderTemplate, runGenerators, toFile } from '@featherscloud/pinion' +import type { AppGeneratorContext } from '../commons.js' +import { dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +// Set __dirname in es module +const __dirname = dirname(fileURLToPath(import.meta.url)) + +const template = ({ sse }: AppGeneratorContext) => /* ts */ `import { feathers } from 'feathers' +import type { HookContext as FeathersHookContext, Application as FeathersApplication } from 'feathers' +import { MessageService } from './services/messages.js' +${sse ? `import { SseService } from './services/sse.js'` : ''} + +export type Services = { + messages: MessageService + ${sse ? `sse: SseService` : ''} +} + +export type Configuration = { +} + +export type Application = FeathersApplication + +export type HookContext = FeathersHookContext + +const app: Application = feathers() + +${sse ? `app.use('sse', new SseService())` : ''} +app.use('messages', new MessageService()) + +export { app } +` + +export const generate = (ctx: AppGeneratorContext) => + Promise.resolve(ctx) + .then(renderTemplate(template, toFile('src', 'app.ts'))) + .then(runGenerators(__dirname, 'services')) diff --git a/packages/create-feathers/src/templates/authenticate.tpl.ts b/packages/create-feathers/src/templates/authenticate.tpl.ts new file mode 100644 index 0000000000..0fc23548e8 --- /dev/null +++ b/packages/create-feathers/src/templates/authenticate.tpl.ts @@ -0,0 +1,23 @@ +import { renderTemplate, toFile } from '@featherscloud/pinion' +import { AppGeneratorContext } from '../commons' + +const template = + ({}: AppGeneratorContext) => /* ts */ `import type { HookContext, NextFunction } from 'feathers' +import { NotAuthenticated } from 'feathers/errors' + +// A hook for simple API key authentication. Extend with the functionality needed for your application. +export async function authenticate(context: HookContext, next: NextFunction) { + if (context.params?.request) { + const { authorization } = context.params?.request.headers['x-api-key'] + + if (authorization !== 'supersecret') { + throw new NotAuthenticated('Invalid API key') + } + } + + await next() +} +` + +export const generate = (ctx: AppGeneratorContext) => + Promise.resolve(ctx).then(renderTemplate(template, toFile('src', 'hooks', 'authenticate.ts'))) diff --git a/packages/create-feathers/src/templates/gitignore.tpl.ts b/packages/create-feathers/src/templates/gitignore.tpl.ts new file mode 100644 index 0000000000..7de1f521e3 --- /dev/null +++ b/packages/create-feathers/src/templates/gitignore.tpl.ts @@ -0,0 +1,128 @@ +import { renderTemplate, toFile } from '@featherscloud/pinion' +import type { AppGeneratorContext } from '../commons.js' + +const template = `# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env.production + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* +.sqlite + +lib/ +` + +export const generate = (ctx: AppGeneratorContext) => + Promise.resolve(ctx).then(renderTemplate(template, toFile('.gitignore'))) diff --git a/packages/create-feathers/src/templates/index.tpl.ts b/packages/create-feathers/src/templates/index.tpl.ts new file mode 100644 index 0000000000..50b9c0186a --- /dev/null +++ b/packages/create-feathers/src/templates/index.tpl.ts @@ -0,0 +1,57 @@ +import { renderTemplate, toFile } from '@featherscloud/pinion' +import type { AppGeneratorContext } from '../commons.js' + +const nodeTemplate = ({}: AppGeneratorContext) => /* ts */ `import { createServer } from 'node:http' +import { createHandler } from 'feathers/http' +import { toNodeHandler } from 'feathers/http/node' +import { app } from './app.js' + +const PORT = process.env.PORT || 3030 +const handler = createHandler(app) +const server = createServer(toNodeHandler(handler)) + +server.listen(PORT, () => { + console.log(\`Server running on http://localhost:\${PORT}\`) +}) + +// Call app.setup to initialize all services +await app.setup(server) +` + +const denoTemplate = ({}: AppGeneratorContext) => /* ts */ `import { createHandler } from 'feathers/http' +import { app } from './app.js' + +const port = Deno.env.get('PORT') || 3030 + +const handler = createHandler(app) + +Deno.serve({ port }, handler) +` + +const bunTemplate = ({}: AppGeneratorContext) => /* ts */ `import { createHandler } from 'feathers/http' +import { app } from './app.js' + +const port = Bun.env.PORT || 3030 +const handler = createHandler(app) + +Bun.serve({ + port, + fetch: handler +}) +` + +const template = (ctx: AppGeneratorContext) => { + switch (ctx.platform) { + case 'deno': + return denoTemplate(ctx) + case 'bun': + return bunTemplate(ctx) + case 'node': + return nodeTemplate(ctx) + default: + throw new Error('Unsupported platform') + } +} + +export const generate = (ctx: AppGeneratorContext) => + Promise.resolve(ctx).then(renderTemplate(template, toFile('src', 'index.ts'))) diff --git a/packages/create-feathers/src/templates/package.json.tpl.ts b/packages/create-feathers/src/templates/package.json.tpl.ts new file mode 100644 index 0000000000..621c617e85 --- /dev/null +++ b/packages/create-feathers/src/templates/package.json.tpl.ts @@ -0,0 +1,22 @@ +import { toFile, writeJSON } from '@featherscloud/pinion' +import type { AppGeneratorContext } from '../commons.js' + +const packageJson = ({ name, description, packager, platform }: AppGeneratorContext) => ({ + name, + description, + version: '0.0.0', + homepage: '', + private: true, + keywords: ['feathers'], + author: {}, + contributors: [] as string[], + bugs: {}, + type: 'module', + feathers: { + packager, + platform + } +}) + +export const generate = (ctx: AppGeneratorContext) => + Promise.resolve(ctx).then(writeJSON(packageJson, toFile('package.json'))) diff --git a/packages/create-feathers/src/templates/prettierrc.tpl.ts b/packages/create-feathers/src/templates/prettierrc.tpl.ts new file mode 100644 index 0000000000..f60e2c2374 --- /dev/null +++ b/packages/create-feathers/src/templates/prettierrc.tpl.ts @@ -0,0 +1,14 @@ +import { renderTemplate, toFile } from '@featherscloud/pinion' +import type { AppGeneratorContext } from '../commons.js' + +const template = `{ + "tabWidth": 2, + "useTabs": false, + "printWidth": 110, + "semi": false, + "trailingComma": "none", + "singleQuote": true +}` + +export const generate = (ctx: AppGeneratorContext) => + Promise.resolve(ctx).then(renderTemplate(template, toFile('.prettierrc'))) diff --git a/packages/create-feathers/src/templates/readme.md.tpl.ts b/packages/create-feathers/src/templates/readme.md.tpl.ts new file mode 100644 index 0000000000..bd45eb0bf1 --- /dev/null +++ b/packages/create-feathers/src/templates/readme.md.tpl.ts @@ -0,0 +1,18 @@ +import { renderTemplate, toFile } from '@featherscloud/pinion' +import type { AppGeneratorContext } from '../commons.js' + +const template = ({ name, description }: AppGeneratorContext) => /* md */ `# ${name} + +> ${description} + +## About + +This project uses [Feathers](http://feathersjs.com). The universal web framework. + +## Help + +For more information on all the things you can do with Feathers visit [feathersjs.com](https://feathersjs.com). +` + +export const generate = (ctx: AppGeneratorContext) => + Promise.resolve(ctx).then(renderTemplate(template, toFile('readme.md'))) diff --git a/packages/create-feathers/src/templates/services/messages.tpl.ts b/packages/create-feathers/src/templates/services/messages.tpl.ts new file mode 100644 index 0000000000..e1522cf6d1 --- /dev/null +++ b/packages/create-feathers/src/templates/services/messages.tpl.ts @@ -0,0 +1,52 @@ +import { renderTemplate, toFile } from '@featherscloud/pinion' +import { AppGeneratorContext } from '../../commons' + +const template = ({}: AppGeneratorContext) => /* ts */ `import type { Params } from 'feathers' +import { hooks } from 'feathers/hooks' + +import { authenticate } from '../hooks/authenticate.js' + +export type Message = { + text: string + completed: boolean +} + +// An example messages Feathers service +@hooks([ + authenticate +]) +export class MessageService { + async find(params: Params) { + return [{ + text: 'Create Feathers app', + completed: true + }, { + text: 'Implement service methods', + completed: false + }] + } + + async get(id: string, params: Params) { + throw new Error('.get not implemented') + } + + async create(data: Message, params: Params) { + throw new Error('.create not implemented') + } + + async update(id: string, data: Message, params: Params) { + throw new Error('.update not implemented') + } + + async patch(id: string, data: Message, params: Params) { + throw new Error('.patch not implemented') + } + + async remove(id: string, params: Params) { + throw new Error('.remove not implemented') + } +} +` + +export const generate = (ctx: AppGeneratorContext) => + Promise.resolve(ctx).then(renderTemplate(template, toFile('src', 'services', 'messages.ts'))) diff --git a/packages/create-feathers/src/templates/services/sse.tpl.ts b/packages/create-feathers/src/templates/services/sse.tpl.ts new file mode 100644 index 0000000000..ba66ba592e --- /dev/null +++ b/packages/create-feathers/src/templates/services/sse.tpl.ts @@ -0,0 +1,21 @@ +import { renderTemplate, toFile, when } from '@featherscloud/pinion' +import { AppGeneratorContext } from '../../commons' + +const template = + ({}: AppGeneratorContext) => /* ts */ `import { SseService as FeathersSseService } from 'feathers/http' +import { hooks } from 'feathers/hooks' + +import { authenticate } from '../hooks/authenticate.js' + +// Extends the Feathers service for server sent events (SSE) to add authentication and other hooks +@hooks([ + authenticate +]) +export class SseService extends FeathersSseService { +} +` + +export const generate = (ctx: AppGeneratorContext) => + Promise.resolve(ctx).then( + when((ctx) => ctx.sse, renderTemplate(template, toFile('src', 'services', 'sse.ts'))) + ) diff --git a/packages/create-feathers/tsconfig.json b/packages/create-feathers/tsconfig.json new file mode 100644 index 0000000000..2ea7169ce3 --- /dev/null +++ b/packages/create-feathers/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig", + "rootDir": "src", + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "src/**/test/**"], + "compilerOptions": { + "outDir": "lib" + } +}