|
| 1 | +# Library Development Guidelines |
| 2 | + |
| 3 | +This document provides guidelines for developing and maintaining this library. It is very important to accurately follow all these guidelines. |
| 4 | + |
| 5 | +## Workflow Guidelines |
| 6 | + |
| 7 | +### Version Control |
| 8 | + |
| 9 | +* Store every user prompt in `prompts.md` immediately upon receiving it: |
| 10 | + ``` |
| 11 | + <previous prompts> |
| 12 | +
|
| 13 | + --- |
| 14 | +
|
| 15 | + <user's prompt verbatim> |
| 16 | + ``` |
| 17 | +* Commit changes after completing any task |
| 18 | +* When committing changes: |
| 19 | + 1. Use a concise title that briefly summarizes the changes |
| 20 | + 2. Include a more detailed summary in the commit body |
| 21 | + 3. Include all user prompts received since the last commit: |
| 22 | + ```bash |
| 23 | + git commit -am "Brief summary title" -m "$(cat <<'EOF' |
| 24 | +Detailed summary of changes. |
| 25 | +
|
| 26 | +<prompt 1> |
| 27 | +
|
| 28 | +<prompt 2> |
| 29 | +
|
| 30 | +<prompt n...> |
| 31 | +EOF |
| 32 | +)" |
| 33 | + ``` |
| 34 | +* Note: `prompts.md` is a local log file and should not be committed to version control |
| 35 | +* Each prompt in `prompts.md` should be separated by `---` surrounded by blank lines |
| 36 | +* Use a single commit command rather than separate add/commit steps |
| 37 | + |
| 38 | +## Project Overview |
| 39 | + |
| 40 | +`@mitranim/js` is a lightweight JavaScript "standard library" that provides utilities across various domains while maintaining these principles: |
| 41 | + |
| 42 | +* Environment-independent (browsers, Deno, Node) |
| 43 | +* No external dependencies |
| 44 | +* No transpilation required |
| 45 | +* No bundlers required |
| 46 | +* Native JS modules only |
| 47 | + |
| 48 | +## Code Organization |
| 49 | + |
| 50 | +The codebase is organized into focused modules: |
| 51 | + |
| 52 | +* `lang.mjs`: Type assertions and core utilities |
| 53 | +* `iter.mjs`: Functional iteration utilities |
| 54 | +* `obj.mjs`: Object manipulation |
| 55 | +* `coll.mjs`: Enhanced data structures |
| 56 | +* Plus specialized modules for DOM, HTTP, paths, etc. |
| 57 | + |
| 58 | +## Code Style Guidelines |
| 59 | + |
| 60 | +### Naming Conventions |
| 61 | + |
| 62 | +* `camelCase` for functions and properties |
| 63 | +* `PascalCase` for classes |
| 64 | +* Type checking functions follow specific patterns: |
| 65 | + * `isThing`: Returns boolean |
| 66 | + * `reqThing`: Requires value to be of type Thing |
| 67 | + * `optThing`: Accepts Thing or null/undefined |
| 68 | + * `onlyThing`: Filters out non-Thing values |
| 69 | + |
| 70 | +### Syntax Conventions |
| 71 | + |
| 72 | +* **No semicolons** — rely on JavaScript's Automatic Semicolon Insertion |
| 73 | +* Use backticks (\`) for string literals even for single words (except in import statements, which require quotes) |
| 74 | +* Use spaces, not tabs (2-space indentation) |
| 75 | +* Prefer single-line conditions when simple: `if (condition) return value` |
| 76 | +* Use braces for multi-line blocks |
| 77 | +* Prefer ternary expressions for simple conditionals: `condition ? valueA : valueB` |
| 78 | +* Prefer early returns over nested if/else structures |
| 79 | +* Use single-line arrow functions without braces for simple returns |
| 80 | +* Multi-line arrow functions use braces and explicit return |
| 81 | +* Avoid extra spacing in single-line parentheses, brackets, and braces: `[1, 2]` not `[ 1, 2 ]` |
| 82 | +* No spaces between parentheses and function bodies: `method() {return value}` not `method() { return value }` |
| 83 | +
|
| 84 | +### Variable Declarations |
| 85 | +
|
| 86 | +* Use `const` for variables that aren't reassigned (preferred) |
| 87 | +* Use `let` for variables that need reassignment |
| 88 | +* Never use `var` |
| 89 | +* Avoid parameter default values with complex expressions |
| 90 | +* Avoid parameter destructuring |
| 91 | + |
| 92 | +### Naming and Organization |
| 93 | + |
| 94 | +* Group related functions together |
| 95 | +* Place internal utility functions at the bottom of files |
| 96 | +* Use consistent prefixes (`is/req/opt/only`) for type-related functions |
| 97 | +* Use descriptive names, with abbreviations marked with "Short for" comments |
| 98 | +* Keep function bodies concise and focused |
| 99 | +* Prefer small, simple, focused functions over large ones — each function should do one thing well |
| 100 | + |
| 101 | +### Programming Paradigm |
| 102 | + |
| 103 | +* Functional programming emphasized |
| 104 | +* Immutability preferred with explicit mutations when needed |
| 105 | +* Minimal dependencies between modules |
| 106 | +* Type safety through runtime checks |
| 107 | +* Prefer statically defined functions over inline closures: |
| 108 | + ```javascript |
| 109 | + // Preferred: Named function definition |
| 110 | + function mapValues(obj) { |
| 111 | + return Object.keys(obj).map(key => transform(obj[key])) |
| 112 | + } |
| 113 | +
|
| 114 | + // Avoid: Anonymous inline closures when reusable |
| 115 | + const result = Object.keys(obj).map(key => transform(obj[key])) |
| 116 | + ``` |
| 117 | +* Take advantage of function hoisting to organize code with important functions at the top: |
| 118 | + ```javascript |
| 119 | + // Main functions at the top (these can call utility functions defined below) |
| 120 | + function processData(data) { |
| 121 | + const validated = validateInput(data) |
| 122 | + return transformData(validated) |
| 123 | + } |
| 124 | +
|
| 125 | + // Utility functions at the bottom |
| 126 | + function validateInput(data) { |
| 127 | + // Implementation details... |
| 128 | + } |
| 129 | +
|
| 130 | + function transformData(data) { |
| 131 | + // Implementation details... |
| 132 | + } |
| 133 | + ``` |
| 134 | + |
| 135 | +### Comments & Documentation |
| 136 | + |
| 137 | +* Use `/* Section */` for grouping related functions |
| 138 | +* Use `// comment` for single-line comments |
| 139 | +* Mark unfinished code with `// TODO: description` |
| 140 | +* Document abbreviations with "Short for" comments |
| 141 | +* Document performance optimizations with comments |
| 142 | +* Document edge cases and platform-specific behavior |
| 143 | + |
| 144 | +### Import & Export Patterns |
| 145 | + |
| 146 | +* Group imports at the top of files |
| 147 | +* Use consistent single-letter aliases (l for lang, i for iter, etc.) |
| 148 | +* Use named exports only, never default exports |
| 149 | +* Export each function individually rather than in groups |
| 150 | +* Maintain minimal cross-module dependencies |
| 151 | + |
| 152 | +## Design Guidelines for New Code |
| 153 | + |
| 154 | +### Function Signatures |
| 155 | +* Use standard variable and parameter names based on role and type: |
| 156 | + |
| 157 | + **For role-based variables (prefer these):** |
| 158 | + * `val` for generic values (when no better name exists) |
| 159 | + * `src` for source data in transformations |
| 160 | + * `tar` for targets of modification |
| 161 | + * `mod` for modifications |
| 162 | + * `opt` for options |
| 163 | + * `ctx` for context |
| 164 | + * `acc` for accumulator |
| 165 | + |
| 166 | + **For type-based variables (when role isn't clear):** |
| 167 | + * `str` for strings |
| 168 | + * `num` for numbers |
| 169 | + * `obj` for objects |
| 170 | + * `arr` for arrays |
| 171 | +
|
| 172 | + **For domain-specific variables**, use short but descriptive names without abbreviation requirements |
| 173 | +
|
| 174 | + **Exceptions:** Single-letter import aliases (`l`, `i`, `o`, `s`, etc.) and the element creator `E` |
| 175 | +
|
| 176 | + See guidelines.md for the complete list |
| 177 | +* Functions should be small, simple, focused and do one thing well |
| 178 | +* Functions should have single purposes with descriptive names |
| 179 | +* Follow established patterns (`is*`, `req*`, `opt*`, `only*`, etc.) |
| 180 | +* Avoid parameter defaults; use separate functions for variant behavior |
| 181 | +
|
| 182 | +### Error Handling |
| 183 | +* Validate parameters early (fail fast) |
| 184 | +* Throw descriptive TypeErrors with informative messages |
| 185 | +* Include value type and expected type in error messages |
| 186 | +* Use error factories from `lang.mjs` when possible |
| 187 | +
|
| 188 | +### Documentation Style |
| 189 | +* Add inline comments for non-obvious code and optimizations |
| 190 | +* Mark unfinished code with `TODO` comments |
| 191 | +* Group related functions with section comments |
| 192 | +* Document edge cases and platform-specific behavior |
| 193 | +* Each exported function/class should have a clear purpose |
| 194 | +* Documentation lives in `/doc/` and is compiled to `/docs/` |
| 195 | +* Include examples for non-obvious functionality |
| 196 | +
|
| 197 | +### Type Safety |
| 198 | +* Prefer tight constraints with explicit validation |
| 199 | +* Validate parameters before using them |
| 200 | +* Use dedicated type validators from `lang.mjs` |
| 201 | +* Use the consistent pattern for type validation variants: |
| 202 | + * `isThing` — Returns boolean indicating if value matches type |
| 203 | + * `reqThing` — Requires value to match type, throws otherwise |
| 204 | + * `optThing` — Accepts the type or null/undefined |
| 205 | + * `onlyThing` — Returns value if it matches type, otherwise undefined |
| 206 | + * `laxThing` — Returns default value if nil, otherwise requires type |
| 207 | +* Place validation at the beginning of functions with early returns |
| 208 | +
|
| 209 | +### Return Values |
| 210 | +* Functions return validated value when successful |
| 211 | +* Methods often return `this` for chaining |
| 212 | +* Transformational functions return new values (don't modify inputs) |
| 213 | +* Mutation functions should be explicit and return the modified object |
| 214 | + |
| 215 | +### Code Organization |
| 216 | +* Group related functions together |
| 217 | +* Keep internal helpers private (non-exported) |
| 218 | +* Separate type checking from business logic |
| 219 | +* Use small, focused classes with single responsibilities |
| 220 | + |
| 221 | +### Performance Considerations |
| 222 | +* Prioritize performance with carefully documented optimizations |
| 223 | +* Use null prototype objects with `Emp()` for dictionaries instead of plain objects |
| 224 | +* Minimize unnecessary object creation and property access |
| 225 | +* Use symbols for private properties |
| 226 | +* Prefer while loops with pre-increment for performance-critical iterations: |
| 227 | + ```javascript |
| 228 | + // More efficient loop with cached length and pre-increment |
| 229 | + let ind = -1 |
| 230 | + const len = arr.length |
| 231 | + while (++ind < len) { |
| 232 | + const val = arr[ind] |
| 233 | + // Process val |
| 234 | + } |
| 235 | + ``` |
| 236 | +* Cache array/string length in variables when iterating in loops |
| 237 | +* Consider memory usage and garbage collection patterns |
| 238 | +* Use `isX` type checks before expensive operations when possible |
| 239 | +* Consider adding `// TODO tune perf` comments for future optimization points |
| 240 | +
|
| 241 | +### Class Implementation |
| 242 | +* Use class declarations for objects with behavior |
| 243 | +* Extend `l.Emp()` instead of `Object` for lightweight inheritance |
| 244 | +* Use method shorthand syntax in class bodies |
| 245 | +* Use explicit getter/setter syntax for properties with computed values |
| 246 | +* Define instance methods with standard syntax, not arrow functions |
| 247 | +* Use static methods for factory patterns and class-related utilities |
| 248 | +* Make chainable methods return `this` |
| 249 | +
|
| 250 | +## Build & Test Commands |
| 251 | +
|
| 252 | +```sh |
| 253 | +# Testing |
| 254 | +make test # Run all tests |
| 255 | +make test_w # Watch mode for tests |
| 256 | +make feat=lang test # Test specific module |
| 257 | +
|
| 258 | +# Linting |
| 259 | +make lint # Run linters (deno & eslint) |
| 260 | +make lint_w # Watch mode for linting |
| 261 | +
|
| 262 | +# Documentation |
| 263 | +make doc # Generate documentation |
| 264 | +make doc_w # Watch mode for documentation |
| 265 | +
|
| 266 | +# Development |
| 267 | +make watch # Watch mode for tests, lint, and docs |
| 268 | +make prep # Test, lint, and generate docs |
| 269 | +
|
| 270 | +# Publishing |
| 271 | +make pub # Tag and push for publishing |
| 272 | +``` |
| 273 | +
|
| 274 | +## Testing Approach |
| 275 | +
|
| 276 | +* Uses a custom test framework defined in `test.mjs` |
| 277 | +* Each module has corresponding test file in `/test/` directory |
| 278 | +* Test files follow naming pattern: `modulename_test.mjs` |
| 279 | +* Test assertions use `t.eq()`, `t.ok()`, `t.throws()`, etc. |
| 280 | +* Benchmarks are available with `make bench` |
| 281 | +
|
| 282 | +### Testing Best Practices |
| 283 | +
|
| 284 | +* Test type validations with both valid and invalid inputs |
| 285 | +* Test error handling with expected exceptions |
| 286 | +* Verify immutability of operations where expected |
| 287 | +* Use `t.is` for comparing primitive values (numbers, strings, booleans) |
| 288 | +* Use `t.eq` only for deep equality comparisons of objects and arrays |
| 289 | +* Test both normal and edge cases for functions |
| 290 | +* Test compatibility across supported environments |
0 commit comments