Skip to content

Code Style Guide

Code style and formatting guidelines for nuxt-openapi-hyperfetch.

General Principles

  • Consistency - Follow existing patterns
  • Readability - Code is read more than written
  • Simplicity - Prefer simple solutions
  • TypeScript - Use types everywhere

TypeScript

Type Annotations

typescript
// ✅ Good - explicit return type
function parseSpec(input: string): ParsedSpec {
  return { /* ... */ }
}

// ❌ Bad - implicit return type
function parseSpec(input: string) {
  return { /* ... */ }
}

Interfaces vs Types

typescript
// ✅ Good - use interface for objects
interface Pet {
  id: number
  name: string
}

// ✅ Good - use type for unions
type Status = 'available' | 'pending' | 'sold'

// ❌ Bad - type for object shape
type Pet = {
  id: number
  name: string
}

Avoid any

typescript
// ✅ Good - specific type
function processData(data: Pet): void { }

// ❌ Bad - any type
function processData(data: any): void { }

// ✅ Acceptable - unknown for truly unknown data
function processData(data: unknown): void {
  if (isPet(data)) {
    // Type guard narrows to Pet
  }
}

Naming Conventions

Variables and Functions

typescript
// camelCase for variables and functions
const petName = 'Fluffy'
function getPetById(id: number) { }

Classes and Interfaces

typescript
// PascalCase for classes and interfaces
class PetParser { }
interface Pet { }

Constants

typescript
// UPPER_SNAKE_CASE for constants
const MAX_RETRIES = 3
const API_BASE_URL = 'https://api.example.com'

Private Members

typescript
class PetService {
  // # for private fields
  #apiKey: string
  
  // _ prefix for private methods (if # not available)
  private _fetchData() { }
}

File Organization

Imports

typescript
// 1. Node built-ins
import { readFile } from 'fs/promises'

// 2. External packages
import { parse } from 'yaml'

// 3. Internal modules
import { parseOpenAPI } from '~/parser'
import { generateCode } from '~/generator'

// 4. Types
import type { OpenAPIObject } from '~/types'

Exports

typescript
// Named exports preferred
export function parseSpec() { }
export class Parser { }

// Default export for single main export
export default class Parser { }

Functions

Function Length

Keep functions short and focused:

typescript
// ✅ Good - single responsibility
function parsePaths(paths: Paths): Operation[] {
  return Object.entries(paths).flatMap(parsePathItem)
}

function parsePathItem([path, item]: [string, PathItem]): Operation[] {
  return Object.entries(item).map(([method, op]) => ({
    path,
    method,
    ...op
  }))
}

// ❌ Bad - too long, multiple responsibilities
function parsePathsAndGenerateCode(paths: Paths): string {
  // 100+ lines of mixed parsing and generation
}

Parameters

typescript
// ✅ Good - max 3 parameters
function generateComposable(
  operation: Operation,
  options: GeneratorOptions
): string

// ❌ Bad - too many parameters
function generateComposable(
  path: string,
  method: string,
  operationId: string,
  parameters: Parameter[],
  requestBody: RequestBody,
  responses: Responses,
  mode: 'client' | 'server'
): string

// ✅ Good - use options object for many parameters
interface GenerateOptions {
  path: string
  method: string
  operationId: string
  // ... more options
}

function generateComposable(options: GenerateOptions): string

Comments

JSDoc

typescript
/**
 * Parse OpenAPI specification from file or URL
 * 
 * @param input - Path to file or URL
 * @returns Parsed OpenAPI specification
 * @throws {Error} If spec is invalid
 * 
 * @example
 * ```ts
 * const spec = await parseOpenAPI('./swagger.yaml')
 * ```
 */
export async function parseOpenAPI(input: string): Promise<ParsedSpec> {
  // Implementation
}

Inline Comments

typescript
// ✅ Good - explain WHY, not WHAT
// Use exponential backoff to avoid rate limiting
await retry(fetchData, { delay: retries => Math.pow(2, retries) * 1000 })

// ❌ Bad - obvious from code
// Call retry function with fetchData and options
await retry(fetchData, { delay: retries => Math.pow(2, retries) * 1000 })

Error Handling

Throw Errors

typescript
// ✅ Good - descriptive error
if (!spec.openapi) {
  throw new Error('Invalid OpenAPI spec: missing "openapi" field')
}

// ❌ Bad - generic error
if (!spec.openapi) {
  throw new Error('Invalid spec')
}

Custom Errors

typescript
// ✅ Good - custom error class
class ParseError extends Error {
  constructor(
    message: string,
    public readonly cause?: unknown
  ) {
    super(message)
    this.name = 'ParseError'
  }
}

throw new ParseError('Failed to parse schema', originalError)

Formatting

Line Length

typescript
// ✅ Good - max 100 characters
const result = someFunction(
  firstParameter,
  secondParameter,
  thirdParameter
)

// ❌ Bad - too long
const result = someFunction(firstParameter, secondParameter, thirdParameter, fourthParameter, fifthParameter)

Indentation

typescript
// 2 spaces for indentation
function example() {
  if (condition) {
    doSomething()
  }
}

Trailing Commas

typescript
// ✅ Good - trailing comma
const options = {
  mode: 'client',
  outputDir: './composables',
}

// ❌ Bad - no trailing comma
const options = {
  mode: 'client',
  outputDir: './composables'
}

Async/Await

typescript
// ✅ Good - async/await
async function fetchPet(id: number): Promise<Pet> {
  const response = await fetch(`/pets/${id}`)
  return await response.json()
}

// ❌ Bad - promise chains
function fetchPet(id: number): Promise<Pet> {
  return fetch(`/pets/${id}`)
    .then(response => response.json())
}

Tools

Prettier

json
{
  "semi": false,
  "singleQuote": true,
  "trailingComma": "es5",
  "printWidth": 100,
  "tabWidth": 2
}

ESLint

json
{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier"
  ],
  "rules": {
    "@typescript-eslint/explicit-function-return-type": "warn",
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/no-unused-vars": "error"
  }
}

Running Formatters

bash
# Format code
npm run format

# Check formatting
npm run format:check

# Lint code
npm run lint

# Fix linting issues
npm run lint:fix

Next Steps

Released under the Apache-2.0 License.