diff --git a/packages/cli/src/app/index.ts b/packages/cli/src/app/index.ts index 07eca6d67f..6daf088f0c 100644 --- a/packages/cli/src/app/index.ts +++ b/packages/cli/src/app/index.ts @@ -179,7 +179,7 @@ export const generate = (ctx: AppGeneratorArguments) => ) .then( install(({ language, framework, devDependencies, dependencyVersions }) => { - devDependencies.push('nodemon', 'axios', 'mocha', 'cross-env') + devDependencies.push('nodemon', 'axios', 'mocha', 'cross-env', 'prettier') if (language === 'ts') { devDependencies.push( diff --git a/packages/cli/src/app/templates/client.tpl.ts b/packages/cli/src/app/templates/client.tpl.ts index 3cb29d7c9a..e1f635323e 100644 --- a/packages/cli/src/app/templates/client.tpl.ts +++ b/packages/cli/src/app/templates/client.tpl.ts @@ -6,8 +6,8 @@ const template = ({}: AppGeneratorContext) => `import { feathers } from '@feathersjs/feathers' import type { Service, TransportConnection, Params } from '@feathersjs/feathers' -// A mapping of client side services export interface ServiceTypes { + // A mapping of client side services } export const createClient = (connection: TransportConnection) => { diff --git a/packages/cli/src/app/templates/package.json.tpl.ts b/packages/cli/src/app/templates/package.json.tpl.ts index 67b6ca7c6d..47a0cda40b 100644 --- a/packages/cli/src/app/templates/package.json.tpl.ts +++ b/packages/cli/src/app/templates/package.json.tpl.ts @@ -6,6 +6,7 @@ const jsPackageJson = (lib: string) => ({ scripts: { start: `node ${lib}`, dev: `nodemon ${lib}/`, + prettier: 'npx prettier "**/*.js" --write', mocha: 'cross-env NODE_ENV=test mocha test/ --recursive --exit', test: 'npm run mocha' } @@ -16,6 +17,7 @@ const tsPackageJson = (lib: string) => ({ dev: `nodemon -x ts-node ${lib}/index.ts`, compile: 'shx rm -rf lib/ && tsc', start: 'npm run compile && node lib/', + prettier: 'npx prettier "**/*.ts" --write', mocha: 'cross-env NODE_ENV=test mocha test/ --require ts-node/register --recursive --extension .ts --exit', test: 'npm run mocha' diff --git a/packages/cli/src/app/templates/prettierrc.tpl.ts b/packages/cli/src/app/templates/prettierrc.tpl.ts new file mode 100644 index 0000000000..a97512d6b6 --- /dev/null +++ b/packages/cli/src/app/templates/prettierrc.tpl.ts @@ -0,0 +1,14 @@ +import { generator, toFile, writeJSON } from '@feathershq/pinion' +import { AppGeneratorContext } from '../index' +import { PRETTIERRC } from '../../commons' + +export const generate = (ctx: AppGeneratorContext) => + generator(ctx).then( + writeJSON( + (ctx) => ({ + ...PRETTIERRC, + parser: ctx.language === 'ts' ? 'typescript' : 'babel' + }), + toFile('.prettierrc') + ) + ) diff --git a/packages/cli/src/commons.ts b/packages/cli/src/commons.ts index bcae020a9b..21ec41ebe6 100644 --- a/packages/cli/src/commons.ts +++ b/packages/cli/src/commons.ts @@ -1,4 +1,5 @@ import { PackageJson } from 'type-fest' +import { readFile, writeFile } from 'fs/promises' import { Callable, PinionContext, @@ -10,7 +11,7 @@ import { Location } from '@feathershq/pinion' import * as ts from 'typescript' -import prettier from 'prettier' +import prettier, { Options as PrettierOptions } from 'prettier' import path from 'path' export type DependencyVersions = { [key: string]: string } @@ -144,35 +145,80 @@ export const getJavaScript = (typescript: string, options: ts.TranspileOptions = ...options.compilerOptions } }) - const code = fixLocalImports(restoreNewLines(transpiled.outputText)) - return prettier.format(code, { - semi: false, - parser: 'babel', - singleQuote: true - }) + return fixLocalImports(restoreNewLines(transpiled.outputText)) +} + +const getFileName = async ( + target: Callable, + ctx: C +) => `${await getCallable(target, ctx)}.${ctx.language}` + +/** + * The default configuration for prettifying files + */ +export const PRETTIERRC: PrettierOptions = { + tabWidth: 2, + useTabs: false, + printWidth: 110, + semi: false, + trailingComma: 'none', + singleQuote: true } +/* + * Format a source file using Prettier. Will use the local configuration, the settings set in + * `options` or a default configuration + * + * @param target The file to prettify + * @param options The Prettier options + * @returns The updated context + */ +export const prettify = + ( + target: Callable, + options: PrettierOptions = PRETTIERRC + ) => + async (ctx: C) => { + const fileName = await getFileName(target, ctx) + const config = (await prettier.resolveConfig(ctx.cwd)) || options + const content = (await readFile(fileName)).toString() + + try { + await writeFile( + fileName, + await prettier.format(content, { + parser: ctx.language === 'ts' ? 'typescript' : 'babel', + ...config + }) + ) + } catch (error: any) { + throw new Error(`Error prettifying ${fileName}: ${error.message}`) + } + + return ctx + } + /** * Render a source file template for the language set in the context. * * @param templates The JavaScript and TypeScript template to render - * @param toFile The target filename without extension (will be added based on language) + * @param target The target filename without extension (will be added based on language) * @returns The updated context */ export const renderSource = ( template: Callable, - toFile: Callable, + target: Callable, options?: { force: boolean } ) => async (ctx: C) => { const { language } = ctx - const fileName = await getCallable(toFile, ctx) + const fileName = await getFileName(target, ctx) const content = language === 'js' ? getJavaScript(await getCallable(template, ctx)) : template - const renderer = renderTemplate(content, `${fileName}.${language}`, options) + const renderer = renderTemplate(content, fileName, options) - return renderer(ctx) + return renderer(ctx).then(prettify(target)) } /** @@ -195,8 +241,8 @@ export const injectSource = const { language } = ctx const source = language === 'js' && transpile ? getJavaScript(await getCallable(template, ctx)) : template - const toFile = await getCallable(target, ctx) - const injector = inject(source, location, `${toFile}.${language}`) + const fileName = await getFileName(target, ctx) + const injector = inject(source, location, fileName) - return injector(ctx) + return injector(ctx).then(prettify(target)) } diff --git a/packages/cli/src/connection/templates/knex.tpl.ts b/packages/cli/src/connection/templates/knex.tpl.ts index a3e1e50145..d31e12fe48 100644 --- a/packages/cli/src/connection/templates/knex.tpl.ts +++ b/packages/cli/src/connection/templates/knex.tpl.ts @@ -17,7 +17,7 @@ export const ${database} = (app: Application) => { const config = app.get('${database}') const db = knex(config!) - app.set('${database}Client', db); + app.set('${database}Client', db) } ` @@ -30,18 +30,19 @@ const config = app.get('${database}') ${language === 'js' ? 'export default config' : 'module.exports = config'} ` -const configurationTemplate = ({ database }: ConnectionGeneratorContext) => ` ${database}: { - type: 'object', - properties: { - client: { type: 'string' }, - connection: { type: 'string' } - } - },` - +const configurationTemplate = ({ database }: ConnectionGeneratorContext) => `${database}: { + type: 'object', + properties: { + client: { type: 'string' }, + connection: { type: 'string' } + } +},` const importTemplate = ({ database }: ConnectionGeneratorContext) => `import { ${database} } from './${database}'` const configureTemplate = ({ database }: ConnectionGeneratorContext) => `app.configure(${database})` + const toAppFile = toFile(({ lib }) => [lib, 'app']) +const toConfig = toFile(({ lib }) => [lib, 'configuration']) export const generate = (ctx: ConnectionGeneratorContext) => generator(ctx) @@ -68,7 +69,7 @@ export const generate = (ctx: ConnectionGeneratorContext) => injectSource( configurationTemplate, before('authentication: authenticationSettingsSchema'), - toFile(({ lib }) => [lib, 'configuration']), + toConfig, false ) ) diff --git a/packages/cli/test/commons.test.ts b/packages/cli/test/commons.test.ts index 92eee4f6d0..f8bd9fb98a 100644 --- a/packages/cli/test/commons.test.ts +++ b/packages/cli/test/commons.test.ts @@ -2,7 +2,7 @@ import { strictEqual } from 'assert' import { getJavaScript } from '../src/commons' describe('common tests', () => { - it('getJavaScript returns transpiled and prettified JavaScript', () => { + it('getJavaScript returns transpiled JavaScript', () => { const transpiled = getJavaScript( `import bla from 'bla' import something from './file' @@ -20,15 +20,15 @@ const otherThing: string = "Hello" strictEqual( transpiled, - `import bla from 'bla' -import something from './file.js' + `import bla from 'bla'; +import something from './file.js'; -function test(arg) { - bla(something) + function test(arg) { + bla(something); } -// This is a comment -const otherThing = 'Hello' + // This is a comment +const otherThing = "Hello"; ` ) })