Frontend Code Conventions in Macro: A Complete Style Guide for TypeScript and Vite

The Macro front-end enforces strict TypeScript and ESLint rules through Vite, Vitest, and Prettier, requiring absolute imports, kebab-case file names, and comprehensive type safety across all apps/web modules.

The open-source Macro repository (macro-inc/macro) maintains a rigorous set of frontend code conventions designed to keep the web application scalable and maintainable. Built on Vite and TypeScript, the project in apps/web follows enterprise-grade standards for linting, testing, and module organization that every contributor must follow.

Build System and Project Configuration

The Macro frontend uses Vite as its primary build tool. All configuration resides in apps/web/vite.config.ts, which imports shared settings from apps/web/vite.base.ts.

The base configuration establishes critical build parameters including path aliasing and plugin initialization:

// apps/web/vite.base.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';

export const createAppViteConfig = () =>
  defineConfig({
    resolve: {
      alias: {
        '@': path.resolve(__dirname, 'src'), // absolute import alias
      },
    },
    plugins: [vue()],
  });

This setup enforces consistent build behavior across development and production environments while enabling the @/ import alias used throughout the codebase.

TypeScript and Type Safety Standards

All frontend code must be written in TypeScript using .ts or .tsx extensions. The project maintains strict type safety through parser configuration in the ESLint setup.

Key type safety rules include:

  • No explicit any: The @typescript-eslint/no-explicit-any rule prevents loose typing except in whitelisted overrides.
  • No non-null assertions: Usage of the ! operator is forbidden unless explicitly permitted in ESLint configuration overrides.
  • Strict type checking: The parser references project: "./tsconfig.eslint.json" to enable type-aware linting rules.

ESLint Configuration and Linting Rules

The canonical ESLint configuration lives in infra/stacks/fusion-auth/.eslintrc.js and extends to the frontend codebase. The setup uses @typescript-eslint/parser with a comprehensive plugin stack.

Active ESLint plugins and configurations include:

  • eslint:recommended
  • @typescript-eslint/recommended
  • @typescript-eslint/recommended-requiring-type-checking
  • import
  • jest
  • prettier

Critical enforced rules include @typescript-eslint/no-unused-vars for dead code elimination, @typescript-eslint/no-unsafe-member-access for type safety, and import/order for consistent module organization.

Code Formatting with Prettier

Prettier integration runs through the ESLint plugin and eslint-config-prettier. Contributors must format code before committing using the project's task runner commands.

Formatting commands available in the repository:

  • npm run fmt for JavaScript/TypeScript files
  • just fmt as the unified task runner equivalent

This ensures consistent code style across all frontend modules without manual formatting debates.

Testing Standards with Vitest

The Macro frontend uses Vitest for unit and integration testing, configured in apps/web/vitest.config.ts. Tests must be colocated with source files using the *.test.ts naming convention.

// apps/web/vitest.config.ts
import { defineConfig } from 'vitest/config';
import { createAppViteConfig } from './vite.base';

export default defineConfig({
  ...createAppViteConfig(),
  test: {
    globals: true,
    environment: 'jsdom',
  },
});

All tests run in a Node.js environment with jsdom for DOM simulation. The configuration inherits the Vite base settings to ensure test environment parity with production builds.

Import and Module Resolution

Absolute imports are strongly preferred using the @/ alias defined in vite.base.ts. Relative imports are permitted only for sibling files within the same directory.

Preferred pattern:

// Correct: Absolute import
import { unwrap } from '@/lib/utils/unwrap';

Discouraged pattern:

// Avoid: Deep relative paths
import { unwrap } from '../../../lib/utils/unwrap';

This convention prevents fragile relative path chains and clarifies module boundaries across the application structure.

Naming Conventions

The repository follows strict casing rules for file and symbol naming:

  • Files: kebab-case (e.g., my-component.ts, user-service.ts)
  • Functions and constants: camelCase (e.g., getUserData, MAX_RETRY_COUNT)
  • Types and interfaces: PascalCase (e.g., UserProfile, ApiResponse)

Generated SDK code in directories like apps/web/src/lib/service-clients/service-storage/generated/zod.ts demonstrates the PascalCase convention for schema types:

import { z } from 'zod';

export const FileTypeSchema = z.enum([
  'docx',
  'pdf',
  'md',
]);

export type FileType = z.infer<typeof FileTypeSchema>;

Error Handling Patterns

The frontend prefers explicit error handling over null assertions. The codebase provides utility functions like unwrap in apps/web/src/lib/utils/unwrap.ts for type-safe value extraction:

// apps/web/src/lib/utils/unwrap.ts
export function unwrap<T>(value: T | undefined | null, msg = 'Unexpected undefined'): T {
  if (value == null) {
    // eslint-disable-next-line @typescript-eslint/no-throw-literal
    throw new Error(msg);
  }
  return value;
}

This pattern eliminates the need for non-null assertions while providing clear error messages during failures. Async operations use try/catch blocks with structured logging following the tracing style conventions from the Rust backend.

Summary

  • Build Tool: Vite with shared configuration in vite.base.ts and entry point at vite.config.ts
  • Type Safety: Strict TypeScript enforced via @typescript-eslint/parser and type-aware rules
  • Linting: Comprehensive ESLint setup in infra/stacks/fusion-auth/.eslintrc.js with no-explicit-any and no-unused-vars rules
  • Formatting: Prettier integration via ESLint with npm run fmt automation
  • Testing: Vitest with jsdom environment and colocated *.test.ts files
  • Imports: Absolute @/ aliases preferred over relative paths
  • Naming: Kebab-case files, camelCase functions, PascalCase types
  • Error Handling: Utility-based unwrapping instead of non-null assertions

Frequently Asked Questions

How do I configure the import alias in a new Macro frontend module?

Import aliases are defined in apps/web/vite.base.ts within the resolve.alias configuration. The standard @ alias points to the src directory. When adding new path aliases, update both the Vite configuration and the tsconfig.json paths to maintain TypeScript resolution parity.

What ESLint rules are strictly enforced in the Macro frontend?

The configuration in infra/stacks/fusion-auth/.eslintrc.js enforces @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars, @typescript-eslint/no-unsafe-member-access, and import/order. Non-null assertions (!) are forbidden unless explicitly overridden for specific lines or files.

Where should I place unit tests in the Macro repository?

Tests must be colocated with source files using the .test.ts suffix (e.g., utils.ts pairs with utils.test.ts). The Vitest configuration in apps/web/vitest.config.ts automatically discovers these files, running them in a jsdom environment with access to Vite's resolved aliases.

How does Macro handle TypeScript type checking during builds?

Type checking occurs through the ESLint parser configuration using project: "./tsconfig.eslint.json" in the ESLint settings. This enables type-aware linting rules that catch type errors alongside style issues, while Vite handles the actual compilation and bundling separately.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →