Files
interview-demo-code/.cursor/rules/javascript.md
Nikita Kiselev f329bfa9d9
Some checks failed
Telegram Mini App Shop Builder / Compute version metadata (push) Has been cancelled
Telegram Mini App Shop Builder / Run Frontend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run Backend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run PHP_CodeSniffer (push) Has been cancelled
Telegram Mini App Shop Builder / Build module. (push) Has been cancelled
Telegram Mini App Shop Builder / release (push) Has been cancelled
Squashed commit message
2026-03-11 23:00:23 +03:00

5.8 KiB
Raw Blame History

JavaScript/TypeScript Code Style Rules

JavaScript Version

  • ES2020+ features
  • Modern async/await
  • Optional chaining (?.)
  • Nullish coalescing (??)
  • Template literals

Code Style

Variable Declarations

// ✅ Use const by default
const customers = [];
const totalRecords = 0;

// ✅ Use let only when reassignment is needed
let currentPage = 1;
currentPage = 2;

// ❌ Do not use var
var oldVariable = 'bad';

Arrow Functions

// ✅ Preferred for short functions
const filtered = items.filter(item => item.isActive);

// ✅ For object methods
const api = {
  get: async (url) => {
    return await fetch(url);
  }
};

// ✅ For complex logic  use regular functions
function complexCalculation(data) {
  // many lines of code
  return result;
}

Template Literals

// ✅ Preferred
const message = `User ${userId} not found`;
const url = `${baseUrl}/api/${endpoint}`;

// ❌ Do not use concatenation
const message = 'User ' + userId + ' not found';

Optional Chaining

// ✅ Use optional chaining
const name = user?.profile?.name;
const count = data?.items?.length ?? 0;

// ❌ Avoid long nested checks
const name = user && user.profile && user.profile.name;

Nullish Coalescing

// ✅ Use ?? for default values
const page = params.page ?? 1;
const name = user.name ?? 'Unknown';

// ❌ Do not use || for numbers/booleans
const page = params.page || 1; // 0 will be replaced with 1

Destructuring

// ✅ Use destructuring
const { data, totalRecords } = response.data;
const [first, second] = items;

// ✅ In function parameters
function processUser({ id, name, email }) {
  // ...
}

// ✅ With default values
const { page = 1, limit = 20 } = params;

Async/Await

// ✅ Preferred
async function loadCustomers() {
  try {
    const response = await apiGet('getCustomers', params);
    return response.data;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
}

// ❌ Avoid .then() chains
function loadCustomers() {
  return apiGet('getCustomers', params)
    .then(response => response.data)
    .catch(error => console.error(error));
}

Vue.js 3 Composition API

Script Setup

<script setup>
// ✅ Use <script setup>
import { ref, computed, onMounted } from 'vue';
import { apiGet } from '@/utils/http.js';

const customers = ref([]);
const loading = ref(false);

const totalRecords = computed(() => customers.value.length);

async function loadCustomers() {
  loading.value = true;
  try {
    const result = await apiGet('getCustomers');
    customers.value = result.data || [];
  } finally {
    loading.value = false;
  }
}

onMounted(() => {
  loadCustomers();
});
</script>

Reactive State

// ✅ Use ref for primitives
const count = ref(0);
const name = ref('');

// ✅ Use reactive for objects
import { reactive } from 'vue';
const state = reactive({
  customers: [],
  loading: false
});

// ✅ Or ref for objects (preferred)
const state = ref({
  customers: [],
  loading: false
});

Computed Properties

// ✅ Use computed for derived values
const filteredCustomers = computed(() => {
  return customers.value.filter(c => c.isActive);
});

// ❌ Do not use methods for derived values
function filteredCustomers() {
  return customers.value.filter(c => c.isActive);
}

Props

<script setup>
// ✅ Define props with types
const props = defineProps({
  customerId: {
    type: Number,
    required: true
  },
  showDetails: {
    type: Boolean,
    default: false
  }
});
</script>

Emits

<script setup>
// ✅ Define emits
const emit = defineEmits(['update', 'delete']);

function handleUpdate() {
  emit('update', data);
}
</script>

Pinia Stores

// ✅ Use setup stores
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useCustomersStore = defineStore('customers', () => {
  const customers = ref([]);
  const loading = ref(false);
  
  const totalRecords = computed(() => customers.value.length);
  
  async function loadCustomers() {
    loading.value = true;
    try {
      const result = await apiGet('getCustomers');
      customers.value = result.data || [];
    } finally {
      loading.value = false;
    }
  }
  
  return {
    customers,
    loading,
    totalRecords,
    loadCustomers
  };
});

Error Handling

// ✅ Always handle errors
async function loadData() {
  try {
    const result = await apiGet('endpoint');
    if (result.success) {
      return result.data;
    } else {
      throw new Error(result.error);
    }
  } catch (error) {
    console.error('Failed to load data:', error);
    toast.error('Failed to load data');
    throw error;
  }
}

Naming Conventions

Variables and Functions

// ✅ camelCase
const customerData = {};
const totalRecords = 0;
function loadCustomers() {}

// ✅ Constants in UPPER_SNAKE_CASE
const MAX_RETRIES = 3;
const API_BASE_URL = '/api';

Components

<!--  PascalCase for components -->
<CustomerCard />
<ProductsList />

Files

// ✅ Use kebab-case for files
// customers-view.vue
// http-utils.js
// customer-service.js

Imports

// ✅ Group imports
// 1. Vue core
import { ref, computed, onMounted } from 'vue';

// 2. Third-party
import { apiGet } from '@/utils/http.js';
import { useToast } from 'primevue';

// 3. Local components
import CustomerCard from '@/components/CustomerCard.vue';

// 4. Types (if using TypeScript)
import type { Customer } from '@/types';

TypeScript (where used)

// ✅ Use types
interface Customer {
  id: number;
  name: string;
  email?: string;
}

function getCustomer(id: number): Promise<Customer> {
  return apiGet(`customers/${id}`);
}