fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #3197 from hey-api/chore/editorconfig-hbs

feat(config): add `oxfmt` preset

authored by

Lubos and committed by
GitHub
0917dfb1 4cca04b0

+224 -87
+5
.changeset/large-plums-sleep.md
··· 1 + --- 2 + '@hey-api/openapi-ts': patch 3 + --- 4 + 5 + **output**: add `oxfmt` preset
-4
.editorconfig
··· 7 7 indent_style = space 8 8 insert_final_newline = true 9 9 trim_trailing_whitespace = true 10 - 11 - [*.hbs] 12 - indent_size = 2 13 - indent_style = space
+1 -2
dev/openapi-ts.config.ts
··· 116 116 // // name: '{{name}}.renamed', 117 117 // suffix: '.meh', 118 118 // }, 119 - // format: 'prettier', 120 119 // header: null, 121 120 header: [ 122 121 '/* eslint-disable */', ··· 124 123 ], 125 124 // importFileExtension: '.js', 126 125 // indexFile: false, 127 - // lint: 'eslint', 128 126 // nameConflictResolver({ attempt, baseName }) { 129 127 // // console.log('resolving conflict for:', { attempt, baseName }); 130 128 // return attempt === 0 ? baseName : `${baseName}_N${attempt + 1}`; 131 129 // }, 132 130 path: path.resolve(__dirname, '.gen'), 131 + postProcess: ['eslint'], 133 132 // preferExportAll: true, 134 133 resolveModuleName: (moduleName) => { 135 134 if (moduleName === 'valibot') {
+34 -29
docs/openapi-ts/configuration/output.md
··· 214 214 }; 215 215 ``` 216 216 217 - ## Format 217 + ## Post Process 218 218 219 - To format your output folder contents, set `format` to a valid formatter. 219 + Post-processing allows you to run commands on the generated output folder after files are written. This is typically used to run formatters, linters, or other cleanup tools. 220 + 221 + Commands are executed in order, and each command receives the output path via the `path` placeholder. 222 + 223 + ### Presets 224 + 225 + You can use built-in presets for common tools: 220 226 221 227 ::: code-group 222 228 223 - ```js [disabled] 229 + ```js [biome:format] 224 230 export default { 225 231 input: 'hey-api/backend', // sign up at app.heyapi.dev 226 232 output: { 227 - format: null, // [!code ++] 228 233 path: 'src/client', 234 + postProcess: ['biome:format'], // [!code ++] 229 235 }, 230 236 }; 231 237 ``` 232 238 233 - ```js [prettier] 239 + ```js [biome:lint] 234 240 export default { 235 241 input: 'hey-api/backend', // sign up at app.heyapi.dev 236 242 output: { 237 - format: 'prettier', // [!code ++] 238 243 path: 'src/client', 244 + postProcess: ['biome:lint'], // [!code ++] 239 245 }, 240 246 }; 241 247 ``` 242 248 243 - ```js [biome] 249 + ```js [eslint] 244 250 export default { 245 251 input: 'hey-api/backend', // sign up at app.heyapi.dev 246 252 output: { 247 - format: 'biome', // [!code ++] 248 253 path: 'src/client', 254 + postProcess: ['eslint'], // [!code ++] 249 255 }, 250 256 }; 251 257 ``` 252 258 253 - ::: 254 - 255 - You can also prevent your output from being formatted by adding your output path to the formatter's ignore file. 256 - 257 - ## Lint 258 - 259 - To lint your output folder contents, set `lint` to a valid linter. 260 - 261 - ::: code-group 262 - 263 - ```js [disabled] 259 + ```js [oxfmt] 264 260 export default { 265 261 input: 'hey-api/backend', // sign up at app.heyapi.dev 266 262 output: { 267 - lint: null, // [!code ++] 268 263 path: 'src/client', 264 + postProcess: ['oxfmt'], // [!code ++] 269 265 }, 270 266 }; 271 267 ``` 272 268 273 - ```js [eslint] 269 + ```js [oxlint] 274 270 export default { 275 271 input: 'hey-api/backend', // sign up at app.heyapi.dev 276 272 output: { 277 - lint: 'eslint', // [!code ++] 278 273 path: 'src/client', 274 + postProcess: ['oxlint'], // [!code ++] 279 275 }, 280 276 }; 281 277 ``` 282 278 283 - ```js [biome] 279 + ```js [prettier] 284 280 export default { 285 281 input: 'hey-api/backend', // sign up at app.heyapi.dev 286 282 output: { 287 - lint: 'biome', // [!code ++] 288 283 path: 'src/client', 284 + postProcess: ['prettier'], // [!code ++] 289 285 }, 290 286 }; 291 287 ``` 292 288 293 - ```js [oxlint] 289 + ::: 290 + 291 + ### Custom 292 + 293 + You can also provide custom post processors: 294 + 295 + <!-- prettier-ignore-start --> 296 + ```js 294 297 export default { 295 298 input: 'hey-api/backend', // sign up at app.heyapi.dev 296 299 output: { 297 - lint: 'oxlint', // [!code ++] 298 300 path: 'src/client', 301 + postProcess: [{ // [!code ++] 302 + command: 'dprint', // [!code ++] 303 + args: ['fmt', '{{path}}'], // [!code ++] 304 + }], // [!code ++] 299 305 }, 300 306 }; 301 307 ``` 308 + <!-- prettier-ignore-end --> 302 309 303 - ::: 304 - 305 - You can also prevent your output from being linted by adding your output path to the linter's ignore file. 310 + You can skip processing by adding the output path to the tool’s ignore file (for example `.eslintignore` or `.prettierignore`). 306 311 307 312 ## Name Conflicts 308 313
+1 -1
docs/openapi-ts/migrating.md
··· 1506 1506 1507 1507 ### Removed `indent` 1508 1508 1509 - This config option has been removed. Use a [code formatter](/openapi-ts/configuration#formatting) to modify the generated files code style according to your preferences. 1509 + This config option has been removed. Use a [code formatter](/openapi-ts/configuration/output#post-process) to modify the generated files code style according to your preferences. 1510 1510 1511 1511 ## v0.27.24 1512 1512
+76 -2
packages/openapi-ts/src/config/output/config.ts
··· 1 + import { log } from '@hey-api/codegen-core'; 1 2 import ts from 'typescript'; 2 3 3 4 import { findTsConfigPath, loadTsConfig } from '~/generate/tsConfig'; 4 5 import type { Config, UserConfig } from '~/types/config'; 5 6 6 7 import { valueToObject } from '../utils/config'; 8 + import type { PostProcessor, UserPostProcessor } from './postprocess'; 9 + import { postProcessors } from './postprocess'; 7 10 import { resolveSource } from './source/config'; 11 + import type { UserOutput } from './types'; 8 12 9 13 export function getOutput(userConfig: UserConfig): Config['output'] { 10 14 if (userConfig.output instanceof Array) { ··· 13 17 ); 14 18 } 15 19 20 + const userOutput = 21 + typeof userConfig.output === 'string' 22 + ? { path: userConfig.output } 23 + : (userConfig.output ?? {}); 24 + 25 + const legacyPostProcess = resolveLegacyPostProcess(userOutput); 26 + 16 27 const output = valueToObject({ 17 28 defaultValue: { 18 29 clean: true, ··· 26 37 indexFile: true, 27 38 lint: null, 28 39 path: '', 40 + postProcess: [], 29 41 preferExportAll: false, 30 42 }, 31 43 mappers: { ··· 45 57 value: fields.fileName, 46 58 }), 47 59 }), 48 - string: (path) => ({ path }), 49 60 }, 50 - value: userConfig.output, 61 + value: userOutput, 51 62 }) as Config['output']; 52 63 output.tsConfig = loadTsConfig(findTsConfigPath(output.tsConfigPath)); 53 64 if ( ··· 65 76 ) { 66 77 output.importFileExtension = `.${output.importFileExtension}`; 67 78 } 79 + output.postProcess = normalizePostProcess( 80 + userOutput.postProcess ?? legacyPostProcess, 81 + ); 68 82 output.source = resolveSource(output); 69 83 return output; 70 84 } 85 + 86 + function resolveLegacyPostProcess( 87 + config: Partial<UserOutput>, 88 + ): ReadonlyArray<UserPostProcessor> { 89 + const result: Array<UserPostProcessor> = []; 90 + 91 + if (config.lint !== undefined) { 92 + let processor: PostProcessor | undefined; 93 + let preset: keyof typeof postProcessors | undefined; 94 + if (config.lint) { 95 + preset = config.lint === 'biome' ? 'biome:lint' : config.lint; 96 + processor = postProcessors[preset]; 97 + if (processor) result.push(processor); 98 + } 99 + 100 + log.warnDeprecated({ 101 + context: 'output', 102 + field: 'lint', 103 + replacement: `postProcess: [${processor && preset ? `'${preset}'` : ''}]`, 104 + }); 105 + } 106 + 107 + if (config.format !== undefined) { 108 + let processor: PostProcessor | undefined; 109 + let preset: keyof typeof postProcessors | undefined; 110 + if (config.format) { 111 + preset = config.format === 'biome' ? 'biome:format' : config.format; 112 + processor = postProcessors[preset]; 113 + if (processor) result.push(processor); 114 + } 115 + 116 + log.warnDeprecated({ 117 + context: 'output', 118 + field: 'format', 119 + replacement: `postProcess: [${processor && preset ? `'${preset}'` : ''}]`, 120 + }); 121 + } 122 + 123 + return result; 124 + } 125 + 126 + function normalizePostProcess( 127 + input: UserOutput['postProcess'], 128 + ): ReadonlyArray<PostProcessor> { 129 + if (!input) return []; 130 + 131 + return input.map((item) => { 132 + if (typeof item === 'string') { 133 + const preset = postProcessors[item]; 134 + if (!preset) { 135 + throw new Error(`Unknown post-processor preset: "${item}"`); 136 + } 137 + return preset; 138 + } 139 + return { 140 + name: item.name ?? item.command, 141 + ...item, 142 + }; 143 + }); 144 + }
+1 -1
packages/openapi-ts/src/config/output/index.ts
··· 1 1 export { getOutput } from './config'; 2 2 export { postprocessOutput } from './postprocess'; 3 - export type { Formatters, Linters, Output, UserOutput } from './types'; 3 + export type { Output, UserOutput } from './types';
+79 -42
packages/openapi-ts/src/config/output/postprocess.ts
··· 1 + import colors from 'ansi-colors'; 1 2 import { sync } from 'cross-spawn'; 2 3 3 - import type { Formatters, Linters, Output } from './types'; 4 + import type { Output } from './types'; 5 + 6 + /** 7 + * @deprecated Use `PostProcessorPreset` instead. 8 + */ 9 + export type Formatters = 'biome' | 'prettier'; 10 + 11 + /** 12 + * @deprecated Use `PostProcessorPreset` instead. 13 + */ 14 + export type Linters = 'biome' | 'eslint' | 'oxlint'; 15 + 16 + export type UserPostProcessor = { 17 + /** 18 + * Arguments to pass to the command. Use `{{path}}` as a placeholder 19 + * for the output directory path. 20 + * 21 + * @example ['format', '--write', '{{path}}'] 22 + */ 23 + args: ReadonlyArray<string>; 24 + /** 25 + * The command to run (e.g., 'biome', 'prettier', 'eslint'). 26 + */ 27 + command: string; 28 + /** 29 + * Display name for logging. Defaults to the command name. 30 + */ 31 + name?: string; 32 + }; 4 33 5 - type OutputProcessor = { 6 - args: (path: string) => ReadonlyArray<string>; 34 + export type PostProcessor = { 35 + /** 36 + * Arguments to pass to the command. 37 + */ 38 + args: ReadonlyArray<string>; 39 + /** 40 + * The command to run. 41 + */ 7 42 command: string; 43 + /** 44 + * Display name for logging. 45 + */ 8 46 name: string; 9 47 }; 10 48 11 - /** 12 - * Map of supported formatters 13 - */ 14 - const formatters: Record<Formatters, OutputProcessor> = { 15 - biome: { 16 - args: (path) => ['format', '--write', path], 49 + export const postProcessors = { 50 + 'biome:format': { 51 + args: ['format', '--write', '{{path}}'], 17 52 command: 'biome', 18 53 name: 'Biome (Format)', 19 54 }, 55 + 'biome:lint': { 56 + args: ['lint', '--apply', '{{path}}'], 57 + command: 'biome', 58 + name: 'Biome (Lint)', 59 + }, 60 + eslint: { 61 + args: ['{{path}}', '--fix'], 62 + command: 'eslint', 63 + name: 'ESLint', 64 + }, 65 + oxfmt: { 66 + args: ['{{path}}'], 67 + command: 'oxfmt', 68 + name: 'Oxfmt', 69 + }, 70 + oxlint: { 71 + args: ['--fix', '{{path}}'], 72 + command: 'oxlint', 73 + name: 'Oxlint', 74 + }, 20 75 prettier: { 21 - args: (path) => [ 76 + args: [ 22 77 '--ignore-unknown', 23 - path, 78 + '{{path}}', 24 79 '--write', 25 80 '--ignore-path', 26 81 './.prettierignore', ··· 28 83 command: 'prettier', 29 84 name: 'Prettier', 30 85 }, 31 - }; 86 + } as const satisfies Record<string, PostProcessor>; 32 87 33 - /** 34 - * Map of supported linters 35 - */ 36 - const linters: Record<Linters, OutputProcessor> = { 37 - biome: { 38 - args: (path) => ['lint', '--apply', path], 39 - command: 'biome', 40 - name: 'Biome (Lint)', 41 - }, 42 - eslint: { 43 - args: (path) => [path, '--fix'], 44 - command: 'eslint', 45 - name: 'ESLint', 46 - }, 47 - oxlint: { 48 - args: (path) => ['--fix', path], 49 - command: 'oxlint', 50 - name: 'oxlint', 51 - }, 52 - }; 88 + export type PostProcessorPreset = keyof typeof postProcessors; 53 89 54 - export const postprocessOutput = (config: Output): void => { 55 - if (config.lint) { 56 - const module = linters[config.lint]; 57 - console.log(`✨ Running ${module.name}`); 58 - sync(module.command, module.args(config.path)); 59 - } 90 + export const postprocessOutput = (config: Output, jobPrefix: string): void => { 91 + for (const processor of config.postProcess) { 92 + const resolved = 93 + typeof processor === 'string' ? postProcessors[processor] : processor; 60 94 61 - if (config.format) { 62 - const module = formatters[config.format]; 63 - console.log(`✨ Running ${module.name}`); 64 - sync(module.command, module.args(config.path)); 95 + const name = resolved.name ?? resolved.command; 96 + const args = resolved.args.map((arg) => 97 + arg.replace('{{path}}', config.path), 98 + ); 99 + 100 + console.log(`${jobPrefix}🧹 Running ${colors.cyanBright(name)}`); 101 + sync(resolved.command, args); 65 102 } 66 103 };
+25 -4
packages/openapi-ts/src/config/output/types.d.ts
··· 8 8 import type { Casing, NameTransformer } from '~/utils/naming'; 9 9 10 10 import type { NamingOptions } from '../shared'; 11 + import type { 12 + Formatters, 13 + Linters, 14 + PostProcessor, 15 + PostProcessorPreset, 16 + UserPostProcessor, 17 + } from './postprocess'; 11 18 import type { SourceConfig, UserSourceConfig } from './source/types'; 12 - 13 - export type Formatters = 'biome' | 'prettier'; 14 - 15 - export type Linters = 'biome' | 'eslint' | 'oxlint'; 16 19 17 20 type ImportFileExtensions = '.js' | '.ts'; 18 21 ··· 76 79 * Which formatter to use to process output folder? 77 80 * 78 81 * @default null 82 + * @deprecated Use `postProcess` instead. 79 83 */ 80 84 format?: Formatters | null; 81 85 /** ··· 103 107 * Which linter to use to process output folder? 104 108 * 105 109 * @default null 110 + * @deprecated Use `postProcess` instead. 106 111 */ 107 112 lint?: Linters | null; 108 113 /** ··· 115 120 */ 116 121 path: string; 117 122 /** 123 + * Post-processing commands to run on the output folder, executed in order. 124 + * 125 + * Use preset strings for common tools, or provide custom configurations. 126 + * 127 + * @example ['biome:lint', 'prettier'] 128 + * @example [{ command: 'dprint', args: ['fmt', '{{path}}'] }] 129 + * @example ['eslint', { command: 'prettier', args: ['{{path}}', '--write'] }] 130 + * 131 + * @default [] 132 + */ 133 + postProcess?: ReadonlyArray<PostProcessorPreset | UserPostProcessor>; 134 + /** 118 135 * Whether `export * from 'module'` should be used when possible 119 136 * instead of named exports. 120 137 * ··· 214 231 * The absolute path to the output folder. 215 232 */ 216 233 path: string; 234 + /** 235 + * Post-processing commands to run on the output folder, executed in order. 236 + */ 237 + postProcess: ReadonlyArray<PostProcessor>; 217 238 /** 218 239 * Whether `export * from 'module'` should be used when possible 219 240 * instead of named exports.
+2 -2
packages/openapi-ts/src/createClient.ts
··· 327 327 328 328 const eventPostprocess = logger.timeEvent('postprocess'); 329 329 if (!config.dryRun) { 330 - postprocessOutput(config.output); 330 + const jobPrefix = colors.gray(`[Job ${jobIndex + 1}] `); 331 + postprocessOutput(config.output, jobPrefix); 331 332 332 333 if (config.logs.level !== 'silent') { 333 334 const outputPath = process.env.INIT_CWD 334 335 ? `./${path.relative(process.env.INIT_CWD, config.output.path)}` 335 336 : config.output.path; 336 - const jobPrefix = colors.gray(`[Job ${jobIndex + 1}] `); 337 337 console.log( 338 338 `${jobPrefix}${colors.green('✅ Done!')} Your output is in ${colors.cyanBright(outputPath)}`, 339 339 );