diff --git a/CLAUDE.md b/CLAUDE.md index 977dbc5ce..f8b0295e0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co 2. KISS (Keep It Simple, Stupid): Aim for simplicity and clarity in code. Avoid unnecessary complexity and abstractions. 3. DRY (Don't Repeat Yourself): Reuse code where possible. Create utility functions or services for common logic, but avoid unnecessary abstractions. 4. Confirm understanding before making changes: If you're unsure about the purpose of a piece of code, ask for clarification rather than making assumptions. -5. Use `npm run prettier` and `npm run lint` to format and check code style before committing changes. +5. **ALWAYS** use `npm run checkFile ` on each file you modify to ensure proper formatting and linting. This runs both prettier and lint checks on individual files. Unless you want to lint and format multiple files, then use `npm run prettier` and `npm run lint` instead. 6. When creating html templates, prefer plain html like `` and `
`. Keep CSS styles to a minimum. Keep nesting to a minimum. Keep css classes to a minimum. Use Angular Material components where appropriate, but avoid overusing them. ## Project Overview @@ -38,11 +38,18 @@ npm run lint # Linting # Build for production npm run dist # All platforms Builds (all available in current environment) + +# IMPORTANT: Check individual files before committing +# Example: npm run checkFile src/app/features/tasks/task.service.ts +# Use this command OFTEN when modifying files to ensure code quality +npm run checkFile # Runs prettier and lint on a single file +# executes unit tests of a single spec file +npm run test:file ``` ### Testing -- Unit tests: `npm test` - Uses Jasmine/Karma, tests are co-located with source files (`.spec.ts`) +- Unit tests: `npm test` - Uses Jasmine/Karma, tests are co-located with source files (`.spec.ts`) - E2E tests: `npm run e2e` - Uses Nightwatch, located in `/e2e/src/` - Linting: `npm run lint` - ESLint for TypeScript, Stylelint for SCSS @@ -81,7 +88,7 @@ The app uses NgRx (Redux pattern) for state management. Key state slices: ### Data Sync - Multiple sync providers: Dropbox, WebDAV, local file -- Sync is conflict-aware with vector-clock resolution +- Sync is conflict-aware with vector-clock resolution - All sync operations go through `/src/app/imex/sync/` ## Important Development Notes diff --git a/package.json b/package.json index 04da34691..c1dc63a7d 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,10 @@ "version": "npm run prebuild && npm run release.changelog && node ./tools/bump-android-version.js && git add -A", "prepare": "ts-patch install && npm run plugin-api:build", "prettier": "pretty-quick", + "prettier:file": "prettier --write", + "lint:file": "ng lint --lint-file-patterns", + "test:file": "cross-env TZ='Europe/Berlin' ng test --watch=false --include", + "checkFile": "node tools/check-file.js", "clean:translations": "node ./tools/clean-translations.js", "plugin-api:build": "cd packages/plugin-api && npm run build", "plugin-api:build:watch": "cd packages/plugin-api && npm run build:watch", diff --git a/tools/check-file.js b/tools/check-file.js new file mode 100644 index 000000000..0118d9694 --- /dev/null +++ b/tools/check-file.js @@ -0,0 +1,36 @@ +#!/usr/bin/env node +const { execSync } = require('child_process'); +const path = require('path'); + +const file = process.argv[2]; +if (!file) { + console.error('โŒ Please provide a file path'); + process.exit(1); +} + +// Get absolute path +const absolutePath = path.resolve(file); + +try { + // Run prettier + console.log(`๐ŸŽจ Formatting ${path.basename(file)}...`); + execSync(`npm run prettier:file ${absolutePath}`, { + stdio: 'pipe', + encoding: 'utf8', + }); + + // Run lint + console.log(`๐Ÿ” Linting ${path.basename(file)}...`); + const lintOutput = execSync(`npm run lint:file ${absolutePath}`, { + stdio: 'pipe', + encoding: 'utf8', + }); + + // If we get here, both commands succeeded + console.log(`โœ… ${path.basename(file)} - All checks passed!`); +} catch (error) { + // If there's an error, show the full output + console.error('\nโŒ Errors found:\n'); + console.error(error.stdout || error.stderr || error.message); + process.exit(1); +} diff --git a/tools/test-file.js b/tools/test-file.js new file mode 100644 index 000000000..6ce161de6 --- /dev/null +++ b/tools/test-file.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +const { execSync } = require('child_process'); +const path = require('path'); + +const file = process.argv[2]; +if (!file) { + console.error('โŒ Please provide a test file path'); + process.exit(1); +} + +// Get absolute path +const absolutePath = path.resolve(file); + +try { + console.log(`๐Ÿงช Running tests for ${path.basename(file)}...`); + + // Run the test directly with cross-env + const { execSync: exec } = require('child_process'); + const output = exec( + `./node_modules/.bin/cross-env TZ='Europe/Berlin' ./node_modules/.bin/ng test --watch=false --include="${absolutePath}"`, + { + stdio: 'pipe', + encoding: 'utf8', + shell: true, + timeout: 25000, // 25 second timeout + }, + ); + + // Extract test results + const lines = output.split('\n'); + const successLine = lines.find( + (line) => line.includes('SUCCESS') || line.includes('FAILED'), + ); + const totalLine = lines.find((line) => line.includes('TOTAL:')); + + if (totalLine) { + const match = totalLine.match(/TOTAL: (\d+) (?:SUCCESS|FAILED)/); + if (match) { + const totalTests = match[1]; + if (successLine && successLine.includes('SUCCESS')) { + console.log(`โœ… All ${totalTests} tests passed!`); + } else { + console.log(`โŒ ${totalLine.trim()}`); + } + } else { + console.log(`โœ… Tests completed: ${totalLine.trim()}`); + } + } else if (successLine) { + console.log(`โœ… ${successLine.trim()}`); + } else { + // Fallback - just indicate completion + console.log(`โœ… Tests completed for ${path.basename(file)}`); + } +} catch (error) { + // If there's an error, show the full output + console.error('\nโŒ Test failures:\n'); + console.error(error.stdout || error.stderr || error.message); + process.exit(1); +}