@zenithbuild/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.eslintignore +15 -0
  2. package/.gitattributes +2 -0
  3. package/.github/ISSUE_TEMPLATE/compiler-errors-for-invalid-state-declarations.md +25 -0
  4. package/.github/ISSUE_TEMPLATE/new_ticket.yaml +34 -0
  5. package/.github/pull_request_template.md +15 -0
  6. package/.github/workflows/discord-changelog.yml +141 -0
  7. package/.github/workflows/discord-notify.yml +242 -0
  8. package/.github/workflows/discord-version.yml +195 -0
  9. package/.prettierignore +13 -0
  10. package/.prettierrc +21 -0
  11. package/.zen.d.ts +15 -0
  12. package/LICENSE +21 -0
  13. package/README.md +55 -0
  14. package/app/components/Button.zen +46 -0
  15. package/app/components/Link.zen +11 -0
  16. package/app/favicon.ico +0 -0
  17. package/app/layouts/Main.zen +59 -0
  18. package/app/pages/about.zen +23 -0
  19. package/app/pages/blog/[id].zen +53 -0
  20. package/app/pages/blog/index.zen +32 -0
  21. package/app/pages/dynamic-dx.zen +712 -0
  22. package/app/pages/dynamic-primitives.zen +453 -0
  23. package/app/pages/index.zen +154 -0
  24. package/app/pages/navigation-demo.zen +229 -0
  25. package/app/pages/posts/[...slug].zen +61 -0
  26. package/app/pages/primitives-demo.zen +273 -0
  27. package/assets/logos/0E3B5DDD-605C-4839-BB2E-DFCA8ADC9604.PNG +0 -0
  28. package/assets/logos/760971E5-79A1-44F9-90B9-925DF30F4278.PNG +0 -0
  29. package/assets/logos/8A06ED80-9ED2-4689-BCBD-13B2E95EE8E4.JPG +0 -0
  30. package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.PNG +0 -0
  31. package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.svg +601 -0
  32. package/assets/logos/README.md +54 -0
  33. package/assets/logos/zen.icns +0 -0
  34. package/bun.lock +39 -0
  35. package/compiler/README.md +380 -0
  36. package/compiler/errors/compilerError.ts +24 -0
  37. package/compiler/finalize/finalizeOutput.ts +163 -0
  38. package/compiler/finalize/generateFinalBundle.ts +82 -0
  39. package/compiler/index.ts +44 -0
  40. package/compiler/ir/types.ts +83 -0
  41. package/compiler/legacy/binding.ts +254 -0
  42. package/compiler/legacy/bindings.ts +338 -0
  43. package/compiler/legacy/component-process.ts +1208 -0
  44. package/compiler/legacy/component.ts +301 -0
  45. package/compiler/legacy/event.ts +50 -0
  46. package/compiler/legacy/expression.ts +1149 -0
  47. package/compiler/legacy/mutation.ts +280 -0
  48. package/compiler/legacy/parse.ts +299 -0
  49. package/compiler/legacy/split.ts +608 -0
  50. package/compiler/legacy/types.ts +32 -0
  51. package/compiler/output/types.ts +34 -0
  52. package/compiler/parse/detectMapExpressions.ts +102 -0
  53. package/compiler/parse/parseScript.ts +22 -0
  54. package/compiler/parse/parseTemplate.ts +425 -0
  55. package/compiler/parse/parseZenFile.ts +66 -0
  56. package/compiler/parse/trackLoopContext.ts +82 -0
  57. package/compiler/runtime/dataExposure.ts +291 -0
  58. package/compiler/runtime/generateDOM.ts +144 -0
  59. package/compiler/runtime/generateHydrationBundle.ts +383 -0
  60. package/compiler/runtime/hydration.ts +309 -0
  61. package/compiler/runtime/navigation.ts +432 -0
  62. package/compiler/runtime/thinRuntime.ts +160 -0
  63. package/compiler/runtime/transformIR.ts +256 -0
  64. package/compiler/runtime/wrapExpression.ts +84 -0
  65. package/compiler/runtime/wrapExpressionWithLoop.ts +77 -0
  66. package/compiler/spa-build.ts +1000 -0
  67. package/compiler/test/validate-test.ts +104 -0
  68. package/compiler/transform/generateBindings.ts +47 -0
  69. package/compiler/transform/generateHTML.ts +28 -0
  70. package/compiler/transform/transformNode.ts +126 -0
  71. package/compiler/transform/transformTemplate.ts +38 -0
  72. package/compiler/validate/validateExpressions.ts +168 -0
  73. package/core/index.ts +135 -0
  74. package/core/lifecycle/index.ts +49 -0
  75. package/core/lifecycle/zen-mount.ts +182 -0
  76. package/core/lifecycle/zen-unmount.ts +88 -0
  77. package/core/reactivity/index.ts +54 -0
  78. package/core/reactivity/tracking.ts +167 -0
  79. package/core/reactivity/zen-batch.ts +57 -0
  80. package/core/reactivity/zen-effect.ts +139 -0
  81. package/core/reactivity/zen-memo.ts +146 -0
  82. package/core/reactivity/zen-ref.ts +52 -0
  83. package/core/reactivity/zen-signal.ts +121 -0
  84. package/core/reactivity/zen-state.ts +180 -0
  85. package/core/reactivity/zen-untrack.ts +44 -0
  86. package/docs/COMMENTS.md +111 -0
  87. package/docs/COMMITS.md +36 -0
  88. package/docs/CONTRIBUTING.md +116 -0
  89. package/docs/STYLEGUIDE.md +62 -0
  90. package/package.json +44 -0
  91. package/router/index.ts +76 -0
  92. package/router/manifest.ts +314 -0
  93. package/router/navigation/ZenLink.zen +231 -0
  94. package/router/navigation/index.ts +78 -0
  95. package/router/navigation/zen-link.ts +584 -0
  96. package/router/runtime.ts +458 -0
  97. package/router/types.ts +168 -0
  98. package/runtime/build.ts +17 -0
  99. package/runtime/serve.ts +93 -0
  100. package/scripts/webhook-proxy.ts +213 -0
  101. package/tsconfig.json +28 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Zenith Memo - Computed/Derived Value
3
+ *
4
+ * A memo is a lazily-evaluated, cached computation that automatically
5
+ * tracks its dependencies and only recomputes when those dependencies change.
6
+ *
7
+ * Features:
8
+ * - Lazy evaluation (only computes when read)
9
+ * - Automatic dependency tracking
10
+ * - Cached value until dependencies change
11
+ * - Read-only (no setter)
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const firstName = zenSignal('John')
16
+ * const lastName = zenSignal('Doe')
17
+ *
18
+ * // Memo computes full name, tracks firstName and lastName
19
+ * const fullName = zenMemo(() => `${firstName()} ${lastName()}`)
20
+ *
21
+ * console.log(fullName()) // "John Doe"
22
+ *
23
+ * firstName('Jane')
24
+ * console.log(fullName()) // "Jane Doe" (recomputed)
25
+ * console.log(fullName()) // "Jane Doe" (cached, no recomputation)
26
+ * ```
27
+ */
28
+
29
+ import {
30
+ pushContext,
31
+ popContext,
32
+ cleanupContext,
33
+ trackDependency,
34
+ type TrackingContext,
35
+ type Subscriber
36
+ } from './tracking'
37
+
38
+ /**
39
+ * Memo interface - callable getter
40
+ */
41
+ export interface Memo<T> {
42
+ /** Get the current computed value */
43
+ (): T
44
+ /** Peek at cached value without tracking (may be stale) */
45
+ peek(): T
46
+ }
47
+
48
+ /**
49
+ * Memo state
50
+ */
51
+ interface MemoState<T> {
52
+ /** The computation function */
53
+ fn: () => T
54
+ /** Cached value */
55
+ value: T | undefined
56
+ /** Whether the cached value is valid */
57
+ dirty: boolean
58
+ /** Tracking context for dependency collection */
59
+ context: TrackingContext
60
+ /** Subscribers to this memo */
61
+ subscribers: Set<Subscriber>
62
+ /** Whether this is the first computation */
63
+ initialized: boolean
64
+ }
65
+
66
+ /**
67
+ * Create a memoized computed value
68
+ *
69
+ * @param fn - The computation function
70
+ * @returns A memo that can be read to get the computed value
71
+ */
72
+ export function zenMemo<T>(fn: () => T): Memo<T> {
73
+ const state: MemoState<T> = {
74
+ fn,
75
+ value: undefined,
76
+ dirty: true,
77
+ context: {
78
+ execute: () => markDirty(state),
79
+ dependencies: new Set()
80
+ },
81
+ subscribers: new Set(),
82
+ initialized: false
83
+ }
84
+
85
+ function memo(): T {
86
+ // Track that something is reading this memo
87
+ trackDependency(state.subscribers)
88
+
89
+ // Recompute if dirty
90
+ if (state.dirty) {
91
+ computeMemo(state)
92
+ }
93
+
94
+ return state.value as T
95
+ }
96
+
97
+ // Add peek method
98
+ ;(memo as Memo<T>).peek = function(): T {
99
+ // Return cached value without tracking or recomputing
100
+ if (state.dirty && !state.initialized) {
101
+ computeMemo(state)
102
+ }
103
+ return state.value as T
104
+ }
105
+
106
+ return memo as Memo<T>
107
+ }
108
+
109
+ /**
110
+ * Compute the memo value, tracking dependencies
111
+ */
112
+ function computeMemo<T>(state: MemoState<T>): void {
113
+ // Clean up old dependencies
114
+ cleanupContext(state.context)
115
+
116
+ // Push this memo onto the tracking stack
117
+ pushContext(state.context)
118
+
119
+ try {
120
+ // Compute new value
121
+ state.value = state.fn()
122
+ state.dirty = false
123
+ state.initialized = true
124
+ } finally {
125
+ // Pop from tracking stack
126
+ popContext()
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Mark the memo as dirty (needs recomputation)
132
+ * Called when a dependency changes
133
+ */
134
+ function markDirty<T>(state: MemoState<T>): void {
135
+ if (!state.dirty) {
136
+ state.dirty = true
137
+
138
+ // Notify any effects/memos that depend on this memo
139
+ // Copy to avoid issues during iteration
140
+ const subscribers = [...state.subscribers]
141
+ for (const subscriber of subscribers) {
142
+ subscriber()
143
+ }
144
+ }
145
+ }
146
+
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Zenith Ref - Mutable Reference Container
3
+ *
4
+ * A ref is a mutable container that does NOT trigger reactivity.
5
+ * It's useful for:
6
+ * - Storing DOM element references
7
+ * - Imperative escape hatches
8
+ * - Values that change but shouldn't trigger re-renders
9
+ *
10
+ * Features:
11
+ * - Mutable `.current` property
12
+ * - Does NOT track dependencies
13
+ * - Does NOT trigger effects
14
+ * - Persists across effect re-runs
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // DOM reference
19
+ * const inputRef = zenRef<HTMLInputElement>()
20
+ *
21
+ * // Later, after mount
22
+ * inputRef.current = document.querySelector('input')
23
+ * inputRef.current?.focus()
24
+ *
25
+ * // Mutable value that doesn't trigger reactivity
26
+ * const previousValue = zenRef(0)
27
+ * previousValue.current = count() // No effect triggered
28
+ * ```
29
+ */
30
+
31
+ /**
32
+ * Ref interface - mutable container with .current
33
+ */
34
+ export interface Ref<T> {
35
+ /** The current value */
36
+ current: T
37
+ }
38
+
39
+ /**
40
+ * Create a mutable reference container
41
+ *
42
+ * @param initialValue - The initial value (optional, defaults to undefined)
43
+ * @returns A ref object with a mutable .current property
44
+ */
45
+ export function zenRef<T>(): Ref<T | undefined>
46
+ export function zenRef<T>(initialValue: T): Ref<T>
47
+ export function zenRef<T>(initialValue?: T): Ref<T | undefined> {
48
+ return {
49
+ current: initialValue
50
+ }
51
+ }
52
+
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Zenith Signal - Atomic Reactive Value
3
+ *
4
+ * A signal is the most basic reactive primitive. It holds a single value
5
+ * and notifies subscribers when the value changes.
6
+ *
7
+ * Features:
8
+ * - Getter/setter model
9
+ * - Automatic dependency tracking
10
+ * - Fine-grained reactivity (no component re-rendering)
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const count = zenSignal(0)
15
+ *
16
+ * // Read value
17
+ * console.log(count()) // 0
18
+ *
19
+ * // Write value
20
+ * count(1)
21
+ *
22
+ * // Or use .value
23
+ * count.value = 2
24
+ * console.log(count.value) // 2
25
+ * ```
26
+ */
27
+
28
+ import { trackDependency, notifySubscribers, type Subscriber } from './tracking'
29
+
30
+ /**
31
+ * Signal interface - callable getter/setter with .value accessor
32
+ */
33
+ export interface Signal<T> {
34
+ /** Get the current value (also tracks dependency) */
35
+ (): T
36
+ /** Set a new value */
37
+ (value: T): void
38
+ /** Get/set value via property */
39
+ value: T
40
+ /** Peek at value without tracking */
41
+ peek(): T
42
+ /** Subscribe to changes */
43
+ subscribe(fn: (value: T) => void): () => void
44
+ }
45
+
46
+ /**
47
+ * Internal signal state
48
+ */
49
+ interface SignalState<T> {
50
+ value: T
51
+ subscribers: Set<Subscriber>
52
+ }
53
+
54
+ /**
55
+ * Create a reactive signal
56
+ *
57
+ * @param initialValue - The initial value of the signal
58
+ * @returns A signal that can be read and written
59
+ */
60
+ export function zenSignal<T>(initialValue: T): Signal<T> {
61
+ const state: SignalState<T> = {
62
+ value: initialValue,
63
+ subscribers: new Set()
64
+ }
65
+
66
+ // The signal function - acts as both getter and setter
67
+ function signal(newValue?: T): T {
68
+ if (arguments.length === 0) {
69
+ // Getter - track dependency and return value
70
+ trackDependency(state.subscribers)
71
+ return state.value
72
+ } else {
73
+ // Setter - update value and notify
74
+ const oldValue = state.value
75
+ state.value = newValue as T
76
+
77
+ if (!Object.is(oldValue, newValue)) {
78
+ notifySubscribers(state.subscribers)
79
+ }
80
+
81
+ return state.value
82
+ }
83
+ }
84
+
85
+ // Add .value accessor
86
+ Object.defineProperty(signal, 'value', {
87
+ get() {
88
+ trackDependency(state.subscribers)
89
+ return state.value
90
+ },
91
+ set(newValue: T) {
92
+ const oldValue = state.value
93
+ state.value = newValue
94
+
95
+ if (!Object.is(oldValue, newValue)) {
96
+ notifySubscribers(state.subscribers)
97
+ }
98
+ },
99
+ enumerable: true,
100
+ configurable: false
101
+ })
102
+
103
+ // Add .peek() - read without tracking
104
+ ;(signal as Signal<T>).peek = function(): T {
105
+ return state.value
106
+ }
107
+
108
+ // Add .subscribe() - manual subscription
109
+ ;(signal as Signal<T>).subscribe = function(fn: (value: T) => void): () => void {
110
+ const subscriber: Subscriber = () => fn(state.value)
111
+ state.subscribers.add(subscriber)
112
+
113
+ // Return unsubscribe function
114
+ return () => {
115
+ state.subscribers.delete(subscriber)
116
+ }
117
+ }
118
+
119
+ return signal as Signal<T>
120
+ }
121
+
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Zenith State - Deep Reactive Object
3
+ *
4
+ * Creates a deeply reactive object using Proxy. Any property access
5
+ * is tracked, and any mutation triggers effects.
6
+ *
7
+ * Features:
8
+ * - Deep reactivity via nested Proxies
9
+ * - Automatic dependency tracking on property access
10
+ * - Triggers effects on property mutation
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const user = zenState({
15
+ * name: 'John',
16
+ * address: {
17
+ * city: 'NYC'
18
+ * }
19
+ * })
20
+ *
21
+ * // Access triggers tracking
22
+ * console.log(user.name)
23
+ *
24
+ * // Mutation triggers effects
25
+ * user.name = 'Jane'
26
+ * user.address.city = 'LA'
27
+ * ```
28
+ */
29
+
30
+ import { trackDependency, notifySubscribers, type Subscriber } from './tracking'
31
+
32
+ /**
33
+ * WeakMap to store proxy targets and their subscriber maps
34
+ * Key: target object
35
+ * Value: Map of property key -> subscriber set
36
+ */
37
+ const proxySubscribers = new WeakMap<object, Map<string | symbol, Set<Subscriber>>>()
38
+
39
+ /**
40
+ * WeakMap to store original objects and their proxies
41
+ * Prevents creating multiple proxies for the same object
42
+ */
43
+ const proxyCache = new WeakMap<object, object>()
44
+
45
+ /**
46
+ * Get or create subscriber set for a property
47
+ */
48
+ function getPropertySubscribers(target: object, key: string | symbol): Set<Subscriber> {
49
+ let propertyMap = proxySubscribers.get(target)
50
+
51
+ if (!propertyMap) {
52
+ propertyMap = new Map()
53
+ proxySubscribers.set(target, propertyMap)
54
+ }
55
+
56
+ let subscribers = propertyMap.get(key)
57
+
58
+ if (!subscribers) {
59
+ subscribers = new Set()
60
+ propertyMap.set(key, subscribers)
61
+ }
62
+
63
+ return subscribers
64
+ }
65
+
66
+ /**
67
+ * Check if a value should be wrapped in a proxy
68
+ */
69
+ function shouldProxy(value: unknown): value is object {
70
+ if (value === null || typeof value !== 'object') {
71
+ return false
72
+ }
73
+
74
+ // Don't proxy special objects
75
+ if (value instanceof Date ||
76
+ value instanceof RegExp ||
77
+ value instanceof Map ||
78
+ value instanceof Set ||
79
+ value instanceof WeakMap ||
80
+ value instanceof WeakSet ||
81
+ value instanceof Promise ||
82
+ ArrayBuffer.isView(value)) {
83
+ return false
84
+ }
85
+
86
+ return true
87
+ }
88
+
89
+ /**
90
+ * Create a reactive proxy for an object
91
+ */
92
+ function createReactiveProxy<T extends object>(target: T): T {
93
+ // Check cache first
94
+ const cached = proxyCache.get(target)
95
+ if (cached) {
96
+ return cached as T
97
+ }
98
+
99
+ const proxy = new Proxy(target, {
100
+ get(target, key, receiver) {
101
+ // Track dependency
102
+ const subscribers = getPropertySubscribers(target, key)
103
+ trackDependency(subscribers)
104
+
105
+ const value = Reflect.get(target, key, receiver)
106
+
107
+ // Recursively proxy nested objects
108
+ if (shouldProxy(value)) {
109
+ return createReactiveProxy(value)
110
+ }
111
+
112
+ return value
113
+ },
114
+
115
+ set(target, key, value, receiver) {
116
+ const oldValue = Reflect.get(target, key, receiver)
117
+
118
+ // Unwrap proxies before storing
119
+ const rawValue = value
120
+
121
+ const result = Reflect.set(target, key, rawValue, receiver)
122
+
123
+ // Only notify if value actually changed
124
+ if (!Object.is(oldValue, rawValue)) {
125
+ const subscribers = getPropertySubscribers(target, key)
126
+ notifySubscribers(subscribers)
127
+ }
128
+
129
+ return result
130
+ },
131
+
132
+ deleteProperty(target, key) {
133
+ const hadKey = Reflect.has(target, key)
134
+ const result = Reflect.deleteProperty(target, key)
135
+
136
+ if (hadKey && result) {
137
+ const subscribers = getPropertySubscribers(target, key)
138
+ notifySubscribers(subscribers)
139
+ }
140
+
141
+ return result
142
+ },
143
+
144
+ has(target, key) {
145
+ // Track dependency for 'in' operator
146
+ const subscribers = getPropertySubscribers(target, key)
147
+ trackDependency(subscribers)
148
+
149
+ return Reflect.has(target, key)
150
+ },
151
+
152
+ ownKeys(target) {
153
+ // Track a special 'keys' dependency for iteration
154
+ const subscribers = getPropertySubscribers(target, Symbol.for('zen:keys'))
155
+ trackDependency(subscribers)
156
+
157
+ return Reflect.ownKeys(target)
158
+ }
159
+ })
160
+
161
+ // Cache the proxy
162
+ proxyCache.set(target, proxy)
163
+
164
+ return proxy
165
+ }
166
+
167
+ /**
168
+ * Create a deeply reactive state object
169
+ *
170
+ * @param initialValue - The initial state object
171
+ * @returns A reactive proxy of the object
172
+ */
173
+ export function zenState<T extends object>(initialValue: T): T {
174
+ if (!shouldProxy(initialValue)) {
175
+ throw new Error('zenState requires a plain object or array')
176
+ }
177
+
178
+ return createReactiveProxy(initialValue)
179
+ }
180
+
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Zenith Untrack - Escape Dependency Tracking
3
+ *
4
+ * Allows reading reactive values without creating a dependency.
5
+ * Useful when you need to read a value inside an effect but don't
6
+ * want the effect to re-run when that value changes.
7
+ *
8
+ * Features:
9
+ * - Disables dependency tracking within the callback
10
+ * - Returns the callback's return value
11
+ * - Can be nested
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const count = zenSignal(0)
16
+ * const multiplier = zenSignal(2)
17
+ *
18
+ * zenEffect(() => {
19
+ * // This creates a dependency on 'count'
20
+ * const c = count()
21
+ *
22
+ * // This does NOT create a dependency on 'multiplier'
23
+ * const m = zenUntrack(() => multiplier())
24
+ *
25
+ * console.log(c * m)
26
+ * })
27
+ *
28
+ * count(5) // Effect re-runs
29
+ * multiplier(3) // Effect does NOT re-run
30
+ * ```
31
+ */
32
+
33
+ import { runUntracked } from './tracking'
34
+
35
+ /**
36
+ * Execute a function without tracking dependencies
37
+ *
38
+ * @param fn - The function to execute
39
+ * @returns The return value of the function
40
+ */
41
+ export function zenUntrack<T>(fn: () => T): T {
42
+ return runUntracked(fn)
43
+ }
44
+
@@ -0,0 +1,111 @@
1
+ # Conventional Comments
2
+
3
+ Zenith uses [Conventional Comments](https://conventionalcomments.org/) to provide clear, actionable feedback in pull requests.
4
+
5
+ ## Format
6
+
7
+ ```
8
+ <label> [decorations]: <subject>
9
+
10
+ [discussion]
11
+ ```
12
+
13
+ ## 💫 Quick Reference
14
+ ### Labels
15
+
16
+ | Label | Description | Example Use Case |
17
+ |--------------|------------------------------------------------------|-------------------------------------------|
18
+ | **praise** | Highlight something positive | Great implementation, well-documented code |
19
+ | **nitpick** | Minor suggestions that don't require changes | Variable naming, minor style preferences |
20
+ | **suggestion** | Propose improvements to consider | Alternative approaches, optimizations |
21
+ | **issue** | Highlight problems that need to be resolved | Bugs, errors, critical problems |
22
+ | **question** | Ask for clarification or explanation | Understanding intent, approach questions |
23
+ | **thought** | Share ideas or considerations for the future | Architectural thoughts, future improvements |
24
+ | **chore** | Simple tasks like formatting or typos | Missing semicolons, typos, formatting |
25
+
26
+ ### Decorations
27
+
28
+ | Decoration | Meaning |
29
+ |-------------------|------------------------------------------------------|
30
+ | **(non-blocking)** | Optional feedback - PR can merge without addressing |
31
+ | **(blocking)** | Must be addressed before merge |
32
+ | **(if-minor)** | Address only if it's a quick fix |
33
+
34
+ ## Examples
35
+
36
+ ### Praise
37
+ ```
38
+ praise: Excellent error handling here!
39
+
40
+ This covers all the edge cases I was worried about.
41
+ ```
42
+
43
+ ### Nitpick (non-blocking)
44
+ ```
45
+ nitpick (non-blocking): Consider renaming `temp` to `processedData`
46
+
47
+ While `temp` works, a more descriptive name might help future maintainers.
48
+ ```
49
+
50
+ ### Suggestion
51
+ ```
52
+ suggestion: We could use a Map instead of an Object here for better performance
53
+
54
+ Since we're doing frequent lookups, a Map would give us O(1) access time
55
+ and better memory characteristics for this use case.
56
+ ```
57
+
58
+ ### Issue (blocking)
59
+ ```
60
+ issue (blocking): This will throw when `items` is undefined
61
+
62
+ We need to add a null/undefined check before calling `.map()` on line 42.
63
+ ```
64
+
65
+ ### Question
66
+ ```
67
+ question: Why are we processing this data twice?
68
+
69
+ I see similar logic on lines 15 and 78. Is there a reason we can't
70
+ consolidate these operations?
71
+ ```
72
+
73
+ ### Thought
74
+ ```
75
+ thought: This might be a good candidate for a custom hook in the future
76
+
77
+ Not for this PR, but as we add more components with similar behavior,
78
+ extracting this pattern could be valuable.
79
+ ```
80
+
81
+ ### Chore
82
+ ```
83
+ chore (if-minor): Missing semicolon on line 23
84
+ ```
85
+
86
+ ## Best Practices
87
+
88
+ 1. **Be specific** - Reference line numbers or code sections
89
+ 2. **Be kind** - Remember there's a human on the other side
90
+ 3. **Be clear** - Explain the "why" behind your feedback
91
+ 4. **Use blocking sparingly** - Only for issues that truly need resolution
92
+ 5. **Praise good work** - Positive feedback is valuable!
93
+
94
+ ## Quick Tips for Reviewers
95
+
96
+ - Start with **praise** for good work
97
+ - Use **suggestion** for most feedback (not blocking unless critical)
98
+ - Reserve **issue (blocking)** for bugs or critical problems
99
+ - Use **question** when you don't understand something
100
+ - Mark style preferences as **nitpick (non-blocking)**
101
+
102
+ ## Quick Tips for Authors
103
+
104
+ - Don't take **nitpicks** personally - they're optional
105
+ - Ask for clarification on unclear **questions** or **suggestions**
106
+ - Address all **blocking** comments before requesting re-review
107
+ - Thank reviewers for their time and feedback
108
+
109
+ ---
110
+
111
+ **Remember**: The goal is constructive collaboration, not perfection. Use these conventions to make reviews clearer and more productive for everyone.
@@ -0,0 +1,36 @@
1
+ ## TL;DR
2
+ ***If you find that this is tanking your productivity just use `feat` or `fix` and always apply `!` if pushing breaking changes***
3
+
4
+ ## Why bother with conventional commits?
5
+ - _The most important reason for us is to simplify/automate SemVer and our Release strategy_
6
+ - For this reason, please prefix all commits with one of the below [Prefixes](#prefixes)
7
+
8
+ ## How does this relate to SemVer?
9
+ - `fix` type commits should be translated to `PATCH` releases.
10
+ - `feat` type commits should be translated to `MINOR` releases.
11
+ - Commits with `BREAKING CHANGE` or `!` (e.g. `feat!: extend parser`) in the commits, regardless of type, should be translated to `MAJOR` releases.
12
+
13
+ ## Prefixes
14
+ | Commit Prefix | SemVer Equivalent | Example |
15
+ | --------------- | ----------------- | ------------------------------------ |
16
+ | fix: | PATCH - 0.0.n | fix: html not recognizing state |
17
+ | feat: | MINOR - 0.n.0 | feat: phase 2 event loop |
18
+ | fix!: | MAJOR - n.0.0 | fix!: html not recognizing state |
19
+ | feat!: | MAJOR - n.0.0 | feat!: phase 2 event loop |
20
+ | BREAKING CHANGE | MAJOR - n.0.0 | fix: BREAKING CHANGE component state |
21
+ | BREAKING-CHANGE | MAJOR - n.0.0 | feat: BREAKING-CHANGE build phase 2 |
22
+ | docs: | CHANGELOG | ... |
23
+ | chore: | CHANGELOG | ... |
24
+ | style: | CHANGELOG | ... |
25
+ | test: | CHANGELOG | ... |
26
+ | refactor: | CHANGELOG | ... |
27
+
28
+ ### Glossary
29
+
30
+ - `feat:` - New features
31
+ - `fix:` - Bug fixes
32
+ - `docs:` - Documentation changes
33
+ - `style:` - Code style changes (formatting, no logic change)
34
+ - `refactor:` - Code refactoring
35
+ - `test:` - Adding or updating tests
36
+ - `chore:` - Maintenance tasks