kiru 0.44.4

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 (70) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +5 -0
  3. package/package.json +81 -0
  4. package/src/appContext.ts +186 -0
  5. package/src/cloneVNode.ts +14 -0
  6. package/src/constants.ts +146 -0
  7. package/src/context.ts +56 -0
  8. package/src/dom.ts +712 -0
  9. package/src/element.ts +54 -0
  10. package/src/env.ts +6 -0
  11. package/src/error.ts +85 -0
  12. package/src/flags.ts +15 -0
  13. package/src/form/index.ts +662 -0
  14. package/src/form/types.ts +261 -0
  15. package/src/form/utils.ts +19 -0
  16. package/src/generateId.ts +19 -0
  17. package/src/globalContext.ts +161 -0
  18. package/src/globals.ts +21 -0
  19. package/src/hmr.ts +178 -0
  20. package/src/hooks/index.ts +14 -0
  21. package/src/hooks/useAsync.ts +136 -0
  22. package/src/hooks/useCallback.ts +31 -0
  23. package/src/hooks/useContext.ts +79 -0
  24. package/src/hooks/useEffect.ts +44 -0
  25. package/src/hooks/useEffectEvent.ts +24 -0
  26. package/src/hooks/useId.ts +42 -0
  27. package/src/hooks/useLayoutEffect.ts +47 -0
  28. package/src/hooks/useMemo.ts +33 -0
  29. package/src/hooks/useReducer.ts +50 -0
  30. package/src/hooks/useRef.ts +40 -0
  31. package/src/hooks/useState.ts +62 -0
  32. package/src/hooks/useSyncExternalStore.ts +59 -0
  33. package/src/hooks/useViewTransition.ts +26 -0
  34. package/src/hooks/utils.ts +259 -0
  35. package/src/hydration.ts +67 -0
  36. package/src/index.ts +61 -0
  37. package/src/jsx.ts +11 -0
  38. package/src/lazy.ts +238 -0
  39. package/src/memo.ts +48 -0
  40. package/src/portal.ts +43 -0
  41. package/src/profiling.ts +105 -0
  42. package/src/props.ts +36 -0
  43. package/src/reconciler.ts +531 -0
  44. package/src/renderToString.ts +91 -0
  45. package/src/router/index.ts +2 -0
  46. package/src/router/route.ts +51 -0
  47. package/src/router/router.ts +275 -0
  48. package/src/router/routerUtils.ts +49 -0
  49. package/src/scheduler.ts +522 -0
  50. package/src/signals/base.ts +237 -0
  51. package/src/signals/computed.ts +139 -0
  52. package/src/signals/effect.ts +60 -0
  53. package/src/signals/globals.ts +11 -0
  54. package/src/signals/index.ts +12 -0
  55. package/src/signals/jsx.ts +45 -0
  56. package/src/signals/types.ts +10 -0
  57. package/src/signals/utils.ts +12 -0
  58. package/src/signals/watch.ts +151 -0
  59. package/src/ssr/client.ts +29 -0
  60. package/src/ssr/hydrationBoundary.ts +63 -0
  61. package/src/ssr/index.ts +1 -0
  62. package/src/ssr/server.ts +124 -0
  63. package/src/store.ts +241 -0
  64. package/src/swr.ts +360 -0
  65. package/src/transition.ts +80 -0
  66. package/src/types.dom.ts +1250 -0
  67. package/src/types.ts +209 -0
  68. package/src/types.utils.ts +39 -0
  69. package/src/utils.ts +581 -0
  70. package/src/warning.ts +9 -0
package/src/utils.ts ADDED
@@ -0,0 +1,581 @@
1
+ import { node, nodeToCtxMap, renderMode } from "./globals.js"
2
+ import {
3
+ $CONTEXT_PROVIDER,
4
+ $FRAGMENT,
5
+ $HYDRATION_BOUNDARY,
6
+ booleanAttributes,
7
+ FLAG,
8
+ REGEX_UNIT,
9
+ } from "./constants.js"
10
+ import { unwrap } from "./signals/utils.js"
11
+ import { KaiokenError } from "./error.js"
12
+ import type { AppContext } from "./appContext"
13
+ import { __DEV__ } from "./env.js"
14
+ import { flags } from "./flags.js"
15
+
16
+ export {
17
+ isVNode,
18
+ isFragment,
19
+ isLazy,
20
+ isMemo,
21
+ className,
22
+ isContextProvider,
23
+ isExoticType,
24
+ isVNodeDeleted,
25
+ vNodeContains,
26
+ willMemoBlockUpdate,
27
+ getCurrentVNode,
28
+ getVNodeAppContext,
29
+ commitSnapshot,
30
+ traverseApply,
31
+ postOrderApply,
32
+ findParent,
33
+ propToHtmlAttr,
34
+ propValueToHtmlAttrValue,
35
+ propsToElementAttributes,
36
+ styleObjectToString,
37
+ shallowCompare,
38
+ deepCompare,
39
+ sideEffectsEnabled,
40
+ encodeHtmlEntities,
41
+ noop,
42
+ latest,
43
+ propFilters,
44
+ safeStringify,
45
+ }
46
+
47
+ type VNode = Kaioken.VNode
48
+
49
+ const noop: () => void = Object.freeze(() => {})
50
+
51
+ function className(...classes: (string | false | undefined)[]): string {
52
+ return classes.filter(Boolean).join(" ")
53
+ }
54
+
55
+ /**
56
+ * This is a no-op in production. It is used to get the latest
57
+ * iteration of a component or signal after HMR has happened.
58
+ */
59
+ function latest<T>(thing: T): T {
60
+ let tgt: any = thing
61
+ if (__DEV__) {
62
+ while ("__next" in tgt) {
63
+ tgt = tgt.__next as typeof tgt
64
+ }
65
+ }
66
+ return tgt
67
+ }
68
+
69
+ /**
70
+ * Returns false if called during "stream" or "string" render modes.
71
+ */
72
+ function sideEffectsEnabled(): boolean {
73
+ return renderMode.current === "dom" || renderMode.current === "hydrate"
74
+ }
75
+
76
+ function isVNodeDeleted(vNode: VNode): boolean {
77
+ return flags.get(vNode.flags, FLAG.DELETION)
78
+ }
79
+
80
+ function isVNode(thing: unknown): thing is VNode {
81
+ return typeof thing === "object" && thing !== null && "type" in thing
82
+ }
83
+
84
+ function isExoticType(type: VNode["type"]): type is Kaioken.ExoticSymbol {
85
+ return (
86
+ type === $FRAGMENT ||
87
+ type === $CONTEXT_PROVIDER ||
88
+ type === $HYDRATION_BOUNDARY
89
+ )
90
+ }
91
+
92
+ function isFragment(vNode: VNode): vNode is VNode & { type: typeof $FRAGMENT } {
93
+ return vNode.type === $FRAGMENT
94
+ }
95
+
96
+ function isLazy(vNode: VNode): boolean {
97
+ return (
98
+ typeof vNode.type === "function" &&
99
+ "displayName" in vNode.type &&
100
+ vNode.type.displayName === "Kaioken.lazy"
101
+ )
102
+ }
103
+
104
+ function isMemo(vNode: Kaioken.VNode): boolean {
105
+ return (
106
+ typeof vNode.type === "function" &&
107
+ "displayName" in vNode.type &&
108
+ vNode.type.displayName === "Kaioken.memo"
109
+ )
110
+ }
111
+
112
+ function isContextProvider(
113
+ thing: unknown
114
+ ): thing is VNode & { type: typeof $CONTEXT_PROVIDER } {
115
+ return isVNode(thing) && thing.type === $CONTEXT_PROVIDER
116
+ }
117
+
118
+ function getCurrentVNode(): VNode | null {
119
+ return node.current
120
+ }
121
+
122
+ function getVNodeAppContext(vNode: VNode): AppContext {
123
+ const n = nodeToCtxMap.get(vNode)
124
+ if (!n)
125
+ throw new KaiokenError({
126
+ message: "Unable to find VNode's AppContext.",
127
+ vNode,
128
+ })
129
+ return n
130
+ }
131
+
132
+ function commitSnapshot(vNode: VNode): void {
133
+ vNode.prev = { ...vNode, props: { ...vNode.props }, prev: null }
134
+ vNode.flags = flags.unsetRange(vNode.flags, FLAG.UPDATE, FLAG.DELETION)
135
+ }
136
+
137
+ function vNodeContains(haystack: VNode, needle: VNode): boolean {
138
+ if (haystack === needle) return true
139
+ let checkSiblings = false
140
+ const stack: VNode[] = [haystack]
141
+ while (stack.length) {
142
+ const n = stack.pop()!
143
+ if (n === needle) return true
144
+ n.child && stack.push(n.child)
145
+ checkSiblings && n.sibling && stack.push(n.sibling)
146
+ checkSiblings = true
147
+ }
148
+ return false
149
+ }
150
+
151
+ function willMemoBlockUpdate(root: VNode, target: VNode): boolean {
152
+ let node: VNode | null = target
153
+
154
+ while (
155
+ node &&
156
+ node !== root &&
157
+ flags.get(node.flags, FLAG.HAS_MEMO_ANCESTOR)
158
+ ) {
159
+ const parent = node.parent
160
+ if (
161
+ parent?.isMemoized &&
162
+ parent.prev?.memoizedProps &&
163
+ parent.arePropsEqual!(parent.prev.memoizedProps, parent.props)
164
+ ) {
165
+ return true
166
+ }
167
+
168
+ node = node.parent
169
+ }
170
+
171
+ return false
172
+ }
173
+
174
+ function traverseApply(vNode: VNode, func: (node: VNode) => void): void {
175
+ let applyToSiblings = false
176
+ const nodes: VNode[] = [vNode]
177
+ const apply = (node: VNode) => {
178
+ func(node)
179
+ node.child && nodes.push(node.child)
180
+ applyToSiblings && node.sibling && nodes.push(node.sibling)
181
+ applyToSiblings = true
182
+ }
183
+ while (nodes.length) apply(nodes.shift()!)
184
+ }
185
+
186
+ function postOrderApply(
187
+ tree: VNode,
188
+ callbacks: {
189
+ /** called upon traversing to the next parent, and on the root */
190
+ onAscent: (vNode: VNode) => void
191
+ /** called before traversing to the next parent */
192
+ onBeforeAscent?: (vNode: VNode) => void
193
+ /** called before traversing to the next child */
194
+ onDescent?: (vNode: VNode) => void
195
+ }
196
+ ): void {
197
+ const root = tree
198
+ const rootChild = root.child
199
+ if (!rootChild) {
200
+ callbacks.onAscent(root)
201
+ return
202
+ }
203
+
204
+ callbacks.onDescent?.(root)
205
+ let branch = rootChild
206
+ while (branch) {
207
+ let c = branch
208
+ while (c) {
209
+ if (!c.child) break
210
+ callbacks.onDescent?.(c)
211
+ c = c.child
212
+ }
213
+
214
+ while (c && c !== root) {
215
+ callbacks.onAscent(c)
216
+ if (c.sibling) {
217
+ branch = c.sibling
218
+ break
219
+ }
220
+ callbacks.onBeforeAscent?.(c)
221
+ c = c.parent!
222
+ }
223
+ if (c === root) break
224
+ }
225
+
226
+ callbacks.onAscent(root)
227
+ }
228
+
229
+ function findParent(
230
+ vNode: Kaioken.VNode,
231
+ predicate: (n: Kaioken.VNode) => boolean
232
+ ) {
233
+ let n: Kaioken.VNode | null = vNode.parent
234
+ while (n) {
235
+ if (predicate(n)) return n
236
+ n = n.parent
237
+ }
238
+ return null
239
+ }
240
+
241
+ function compare<T>(a: T, b: T, deep = false): boolean {
242
+ // Fast path: identity comparison
243
+ if (a === b) return true
244
+
245
+ // Handle primitive types and null/undefined
246
+ if (
247
+ a == null ||
248
+ b == null ||
249
+ typeof a !== "object" ||
250
+ typeof b !== "object"
251
+ ) {
252
+ return false
253
+ }
254
+
255
+ // Handle arrays efficiently
256
+ if (Array.isArray(a) && Array.isArray(b)) {
257
+ if (a.length !== b.length) return false
258
+
259
+ if (deep) {
260
+ for (let i = 0; i < a.length; i++) {
261
+ if (!compare(a[i], b[i], true)) return false
262
+ }
263
+ } else {
264
+ for (let i = 0; i < a.length; i++) {
265
+ if (!Object.is(a[i], b[i])) return false
266
+ }
267
+ }
268
+ return true
269
+ }
270
+
271
+ // Handle Maps
272
+ if (a instanceof Map && b instanceof Map) {
273
+ if (a.size !== b.size) return false
274
+
275
+ for (const [key, valueA] of a) {
276
+ if (!b.has(key)) return false
277
+
278
+ const valueB = b.get(key)
279
+ if (deep) {
280
+ if (!compare(valueA, valueB, true)) return false
281
+ } else {
282
+ if (!Object.is(valueA, valueB)) return false
283
+ }
284
+ }
285
+ return true
286
+ }
287
+
288
+ // Handle Sets more efficiently
289
+ if (a instanceof Set && b instanceof Set) {
290
+ if (a.size !== b.size) return false
291
+
292
+ if (deep) {
293
+ // For deep equality of Sets, we need to compare the values themselves
294
+ // Convert to arrays and sort for comparison
295
+ const aValues = Array.from(a)
296
+ const bValues = Array.from(b)
297
+
298
+ if (aValues.length !== bValues.length) return false
299
+
300
+ // Simple compare doesn't work for objects in Sets with deep comparison
301
+ // Using a matching algorithm instead
302
+ for (const valueA of aValues) {
303
+ // Find matching element in bValues
304
+ let found = false
305
+ for (let i = 0; i < bValues.length; i++) {
306
+ if (compare(valueA, bValues[i], true)) {
307
+ bValues.splice(i, 1) // Remove the matched element
308
+ found = true
309
+ break
310
+ }
311
+ }
312
+ if (!found) return false
313
+ }
314
+ return true
315
+ } else {
316
+ // Regular Set comparison
317
+ for (const valueA of a) {
318
+ if (!b.has(valueA)) return false
319
+ }
320
+ return true
321
+ }
322
+ }
323
+
324
+ // Handle Date objects
325
+ if (a instanceof Date && b instanceof Date) {
326
+ return a.getTime() === b.getTime()
327
+ }
328
+
329
+ // Handle RegExp objects
330
+ if (a instanceof RegExp && b instanceof RegExp) {
331
+ return a.toString() === b.toString()
332
+ }
333
+
334
+ // Handle plain objects
335
+ const keysA = Object.keys(a)
336
+ const keysB = Object.keys(b)
337
+
338
+ if (keysA.length !== keysB.length) return false
339
+
340
+ // Use a Set for faster key lookup
341
+ const keySet = new Set(keysB)
342
+
343
+ for (const key of keysA) {
344
+ if (!keySet.has(key)) return false
345
+
346
+ const valueA = a[key as keyof T]
347
+ const valueB = b[key as keyof T]
348
+
349
+ if (deep) {
350
+ if (!compare(valueA, valueB, true)) return false
351
+ } else {
352
+ if (!Object.is(valueA, valueB)) return false
353
+ }
354
+ }
355
+
356
+ return true
357
+ }
358
+
359
+ function deepCompare<T>(a: T, b: T): boolean {
360
+ return compare(a, b, true)
361
+ }
362
+
363
+ function shallowCompare<T>(a: T, b: T): boolean {
364
+ return compare(a, b, false)
365
+ }
366
+
367
+ function encodeHtmlEntities(text: string): string {
368
+ return text
369
+ .replace(REGEX_UNIT.AMP_G, "&amp;")
370
+ .replace(REGEX_UNIT.LT_G, "&lt;")
371
+ .replace(REGEX_UNIT.GT_G, "&gt;")
372
+ .replace(REGEX_UNIT.DBLQT_G, "&quot;")
373
+ .replace(REGEX_UNIT.SQT_G, "&#039;")
374
+ .replace(REGEX_UNIT.SLASH_G, "&#47;")
375
+ }
376
+
377
+ const propFilters = {
378
+ internalProps: ["children", "ref", "key", "innerHTML"],
379
+ isEvent: (key: string) => key.startsWith("on"),
380
+ isProperty: (key: string) =>
381
+ !propFilters.internalProps.includes(key) && !propFilters.isEvent(key),
382
+ }
383
+
384
+ function propToHtmlAttr(key: string): string {
385
+ switch (key) {
386
+ case "className":
387
+ return "class"
388
+ case "htmlFor":
389
+ return "for"
390
+ case "tabIndex":
391
+ case "formAction":
392
+ case "formMethod":
393
+ case "formEncType":
394
+ case "contentEditable":
395
+ case "spellCheck":
396
+ case "allowFullScreen":
397
+ case "autoPlay":
398
+ case "disablePictureInPicture":
399
+ case "disableRemotePlayback":
400
+ case "formNoValidate":
401
+ case "noModule":
402
+ case "noValidate":
403
+ case "popoverTarget":
404
+ case "popoverTargetAction":
405
+ case "playsInline":
406
+ case "readOnly":
407
+ case "itemscope":
408
+ case "rowSpan":
409
+ case "crossOrigin":
410
+ return key.toLowerCase()
411
+
412
+ default:
413
+ if (key.indexOf("-") > -1) return key
414
+ if (key.startsWith("aria"))
415
+ return "aria-" + key.substring(4).toLowerCase()
416
+
417
+ return snakeCaseAttrs.get(key) || key
418
+ }
419
+ }
420
+
421
+ const snakeCaseAttrs = new Map([
422
+ ["acceptCharset", "accept-charset"],
423
+ ["accentHeight", "accent-height"],
424
+ ["alignmentBaseline", "alignment-baseline"],
425
+ ["arabicForm", "arabic-form"],
426
+ ["baselineShift", "baseline-shift"],
427
+ ["capHeight", "cap-height"],
428
+ ["clipPath", "clip-path"],
429
+ ["clipRule", "clip-rule"],
430
+ ["colorInterpolation", "color-interpolation"],
431
+ ["colorInterpolationFilters", "color-interpolation-filters"],
432
+ ["colorProfile", "color-profile"],
433
+ ["colorRendering", "color-rendering"],
434
+ ["dominantBaseline", "dominant-baseline"],
435
+ ["enableBackground", "enable-background"],
436
+ ["fillOpacity", "fill-opacity"],
437
+ ["fillRule", "fill-rule"],
438
+ ["floodColor", "flood-color"],
439
+ ["floodOpacity", "flood-opacity"],
440
+ ["fontFamily", "font-family"],
441
+ ["fontSize", "font-size"],
442
+ ["fontSizeAdjust", "font-size-adjust"],
443
+ ["fontStretch", "font-stretch"],
444
+ ["fontStyle", "font-style"],
445
+ ["fontVariant", "font-variant"],
446
+ ["fontWeight", "font-weight"],
447
+ ["glyphName", "glyph-name"],
448
+ ["glyphOrientationHorizontal", "glyph-orientation-horizontal"],
449
+ ["glyphOrientationVertical", "glyph-orientation-vertical"],
450
+ ["horizAdvX", "horiz-adv-x"],
451
+ ["horizOriginX", "horiz-origin-x"],
452
+ ["httpEquiv", "http-equiv"],
453
+ ["imageRendering", "image-rendering"],
454
+ ["letterSpacing", "letter-spacing"],
455
+ ["lightingColor", "lighting-color"],
456
+ ["markerEnd", "marker-end"],
457
+ ["markerMid", "marker-mid"],
458
+ ["markerStart", "marker-start"],
459
+ ["overlinePosition", "overline-position"],
460
+ ["overlineThickness", "overline-thickness"],
461
+ ["paintOrder", "paint-order"],
462
+ ["panose-1", "panose-1"],
463
+ ["pointerEvents", "pointer-events"],
464
+ ["renderingIntent", "rendering-intent"],
465
+ ["shapeRendering", "shape-rendering"],
466
+ ["stopColor", "stop-color"],
467
+ ["stopOpacity", "stop-opacity"],
468
+ ["strikethroughPosition", "strikethrough-position"],
469
+ ["strikethroughThickness", "strikethrough-thickness"],
470
+ ["strokeDasharray", "stroke-dasharray"],
471
+ ["strokeDashoffset", "stroke-dashoffset"],
472
+ ["strokeLinecap", "stroke-linecap"],
473
+ ["strokeLinejoin", "stroke-linejoin"],
474
+ ["strokeMiterlimit", "stroke-miterlimit"],
475
+ ["strokeOpacity", "stroke-opacity"],
476
+ ["strokeWidth", "stroke-width"],
477
+ ["textAnchor", "text-anchor"],
478
+ ["textDecoration", "text-decoration"],
479
+ ["textRendering", "text-rendering"],
480
+ ["transformOrigin", "transform-origin"],
481
+ ["underlinePosition", "underline-position"],
482
+ ["underlineThickness", "underline-thickness"],
483
+ ["unicodeBidi", "unicode-bidi"],
484
+ ["unicodeRange", "unicode-range"],
485
+ ["unitsPerEm", "units-per-em"],
486
+ ["vAlphabetic", "v-alphabetic"],
487
+ ["vHanging", "v-hanging"],
488
+ ["vIdeographic", "v-ideographic"],
489
+ ["vMathematical", "v-mathematical"],
490
+ ["vectorEffect", "vector-effect"],
491
+ ["vertAdvY", "vert-adv-y"],
492
+ ["vertOriginX", "vert-origin-x"],
493
+ ["vertOriginY", "vert-origin-y"],
494
+ ["wordSpacing", "word-spacing"],
495
+ ["writingMode", "writing-mode"],
496
+ ["xmlnsXlink", "xmlns:xlink"],
497
+ ["xHeight", "x-height"],
498
+ ])
499
+
500
+ function styleObjectToString(obj: Partial<CSSStyleDeclaration>): string {
501
+ let cssString = ""
502
+ for (const key in obj) {
503
+ const cssKey = key.replace(REGEX_UNIT.ALPHA_UPPER_G, "-$&").toLowerCase()
504
+ cssString += `${cssKey}:${obj[key]};`
505
+ }
506
+ return cssString
507
+ }
508
+
509
+ function stylePropToString(style: unknown) {
510
+ if (typeof style === "string") return style
511
+ if (typeof style === "object" && !!style) return styleObjectToString(style)
512
+ return ""
513
+ }
514
+
515
+ function propValueToHtmlAttrValue(key: string, value: unknown): string {
516
+ return key === "style" && typeof value === "object" && !!value
517
+ ? styleObjectToString(value)
518
+ : String(value)
519
+ }
520
+ function propsToElementAttributes(props: Record<string, unknown>): string {
521
+ const attrs: string[] = []
522
+ const { className, style, ...rest } = props
523
+ if (className) {
524
+ const val = unwrap(className)
525
+ if (!!val) attrs.push(`class="${val}"`)
526
+ }
527
+ if (style) {
528
+ const val = unwrap(style)
529
+ if (!!val) attrs.push(`style="${stylePropToString(val)}"`)
530
+ }
531
+
532
+ const keys = Object.keys(rest).filter(propFilters.isProperty)
533
+ for (let i = 0; i < keys.length; i++) {
534
+ let k = keys[i]
535
+ let val = unwrap(props[k])
536
+ if (val === null || val === undefined) continue
537
+
538
+ k = k.split("bind:")[1] ?? k // normalize bind props
539
+ const key = propToHtmlAttr(k)
540
+
541
+ switch (typeof val) {
542
+ case "function":
543
+ case "symbol":
544
+ continue
545
+ case "boolean":
546
+ if (booleanAttributes.has(key)) {
547
+ if (val) attrs.push(key)
548
+ continue
549
+ }
550
+ }
551
+ attrs.push(`${key}="${val}"`)
552
+ }
553
+ return attrs.join(" ")
554
+ }
555
+
556
+ type SafeStringifyOptions = {
557
+ /**
558
+ * By default, functions are stringified. Specify `false` to instead produce `[FUNCTION (${fn.name})]`.
559
+ */
560
+ functions: boolean
561
+ }
562
+
563
+ function safeStringify(
564
+ value: unknown,
565
+ opts: SafeStringifyOptions = { functions: true }
566
+ ): string {
567
+ const seen = new WeakSet()
568
+ return JSON.stringify(value, (_, value) => {
569
+ if (typeof value === "object" && value !== null) {
570
+ if (seen.has(value)) {
571
+ return "[CIRCULAR]"
572
+ }
573
+ seen.add(value)
574
+ }
575
+ if (typeof value === "function") {
576
+ if (!opts.functions) return `[FUNCTION (${value.name || "anonymous"})]`
577
+ return value.toString()
578
+ }
579
+ return value
580
+ })
581
+ }
package/src/warning.ts ADDED
@@ -0,0 +1,9 @@
1
+ export const warnDeprecated = (
2
+ featName: string,
3
+ reason: string,
4
+ solution: string
5
+ ) => {
6
+ console.warn(
7
+ `[kaioken]: "${featName}" is deprecated ${reason} and will be removed in future versions. ${solution}.`
8
+ )
9
+ }