Skip to content

Latest commit

 

History

History
260 lines (207 loc) · 9.8 KB

STYLEGUIDE.md

File metadata and controls

260 lines (207 loc) · 9.8 KB

InSpatial


InSpatial Style Guide

A comprehensive guide for maintaining clean, consistent, and maintainable code across the InSpatial ecosystem

InSpatial Dev InSpatial Cloud InSpatial App InSpatial Store

License Discord Twitter LinkedIn


📋 Table of Contents


💫 Core Principles

Principle Description
Readability First Code should be written with the next developer in mind
Consistency Matters Follow established patterns throughout the project
Self-Explanatory Code Write intuitive code that requires minimal comments
Comprehensive Documentation Document following InSpatial Doc Rules

📚 Code Standards

Standard Description Guidelines
📦 ESM Modules Use ECMAScript Modules exclusively • Avoid CommonJS modules
• Use import/export syntax
🔒 Deno APIs Prefer Deno over Node.js APIs • Use provided Deno API abstractions
• Follow secure practices
📝 File Names Cross-platform compatible naming • Avoid *, :, ?
• No case-only differences
• Use kebab-case
Type Performance Avoid "slow types" • Follow JSR slow types guide
• Use efficient type patterns
• Follow Typescript's Performance Rules
🚫 Dependencies No native binary dependencies • Pure TypeScript preferred
• Use WASM for native functionality

Technical Standards

Category Guidelines Tools & Resources
🎨 Shaders • Use WGSL or TSL
• WebGL 2.0 compatibility
@inspatial/util
🔄 Programming Patterns • Functional programming
• Declarative patterns
Patterns.dev
✍️ Variable Naming • Use auxiliary verbs
• Self-documenting names
Examples:
isLoading
hasError
📁 File Structure 1. Exported components
2. Subcomponents
3. Helpers
4. Static content
5. Types
Keep consistent order

InSpatial Ecosystem

Tool Purpose Usage
InSpatial Kit Component Construction Primary UI building blocks
InSpatial ISS Styling Styling system
InSpatial Util Utilities Common utilities
InSpatial Infetch HTTP Requests API communication
Motion Animations JavaScript animations & transitions

🏷️ Naming Conventions

Type Convention Example Additional Rules
Variables camelCase userData Use descriptive names that convey intent
Components PascalCase UserProfile -
Files/Directories kebab-case user-profile.ts -
Types/Interfaces PascalCase + Prop UserProp Must start with uppercase letter
Private Variables underscore prefix _privateData -
Functions camelCase fetchUserData -
Constants SCREAMING_SNAKE_CASE MAX_RETRY_COUNT -
Boolean Variables camelCase with prefix isLoading, hasError Use prefixes: is, has, should, can, etc.
Event Handlers camelCase with 'handle' prefix handleClick -

General Rules

  • Avoid abbreviations unless widely understood (e.g., id is fine, but usr is not)
  • Names should be self-documenting and clearly indicate purpose
  • Keep naming consistent across related entities

✏️ TypeScript

Type Definitions

Practice Do Don't Reason
Type Annotations function foo(): BazType function foo() Helps compiler work faster with explicit types
Type Composition interface Foo extends Bar, Baz type Foo = Bar & Baz Interfaces create cached, flat object types
Base Types interface Animal { ... } type Animal = Dog | Cat Reduces type comparison complexity
Complex Types type ComplexType = { ... } Inline complex types Named types are more compact and cacheable

Compiler Configuration

Flag Purpose Impact
--incremental Save compilation state Recompiles only changed files
--skipLibCheck Skip .d.ts checking Faster compilation by skipping verified types
--strictFunctionTypes Optimize type checks Reduces assignability checks between types

Best Practices

  • Use explicit return types on exported functions
  • Prefer interfaces over type intersections for better caching
  • Name complex types instead of using anonymous types
  • Use base types instead of large union types
  • Keep type hierarchies shallow when possible
  • Use ES6+ syntax: arrow functions, destructuring, template literals, etc.
  • Avoid any unless absolutely necessary. Use strict and explicit typing.
  • Follow Typescript's Performance Rules

Example

// ✅ Do: Use interfaces and explicit types
interface UserData {
  id: string;
  name: string;
}

// ✅ Do: Use ES6+ syntax with strict typing
function fetchUser(id: string): Promise<UserData> {
  return inFetch(`/users/${id}`);
}
// ❌ Don't: Use type intersections and implicit types
type UserData = BaseUser & {
  extraData: unknown;
}

// ❌ Don't: Use type intersections and implicit types
function fetchUser(id) {
  return inFetch(`/users/${id}`);
}

🧪 Test Structure and Organization

  • Use InSpatial Test for all types of tests
  • Place tests next to the relevant file
  • Use one of these naming patterns:
    • file.test.ts (preferred)
    • file_test.ts
  • Write meaningful test descriptions and cover edge cases
  • Check test coverage using deno test --coverage
  • Follow InSpatial Test Rules.

Running Tests

# Run all tests
deno test

# Run specific test suite
deno test packages/core

# Run with coverage
deno test --coverage

Test Examples

import { test } from "@inspatial/test";

// Prefer object style for tests 

// ✅ Do: Descriptive test names and comprehensive test cases
test({
  name: "Button renders with correct label",
  fn: () => {
    const user = await fetchUser('123');
    expect(user).toHaveProperty('id', '123');
  }
});

describe('fetchUser', () => {
  it('returns a user object when the request is successful', async () => {
    const user = await fetchUser('123');
    expect(user).toHaveProperty('id', '123');
  });

  it('throws an error when the user ID is invalid', async () => {
    await expect(fetchUser('')).rejects.toThrow('Invalid user ID');
  });
});
// ❌ Don't: Vague test names or incomplete coverage
test({
  name: "button test",
  fn: () => {
    // ...
  }
});

📝 Comments

  • When to Comment:
    • Use comments to explain why, not what.
      The code should already explain what it does.
    • Document complex logic or unusual decisions.

Example:

/**
 * Fetches a user by ID from the server.
 *
 * @param id - The ID of the user to fetch.
 * @returns A promise resolving to the user object.
 */
function fetchUser(id: string): Promise<User> {
  return inFetch(`/users/${id}`);
}