bruh 1.9.2-types.0 → 1.10.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.
@@ -1,3 +1,10 @@
1
+ const isMetaNode = Symbol.for("bruh meta node")
2
+ const isMetaTextNode = Symbol.for("bruh meta text node")
3
+ const isMetaElement = Symbol.for("bruh meta element")
4
+ const isMetaRawString = Symbol.for("bruh meta raw string")
5
+
6
+ //#region HTML syntax functions
7
+
1
8
  // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
2
9
  const voidElements = [
3
10
  "base",
@@ -26,6 +33,7 @@ const isVoidElement = element =>
26
33
 
27
34
  // https://html.spec.whatwg.org/multipage/syntax.html#elements-2
28
35
  // https://html.spec.whatwg.org/multipage/syntax.html#cdata-rcdata-restrictions
36
+ // Does not work for https://html.spec.whatwg.org/multipage/syntax.html#raw-text-elements (script and style)
29
37
  const escapeForElement = x =>
30
38
  (x + "")
31
39
  .replace(/&/g, "&")
@@ -37,14 +45,20 @@ const escapeForDoubleQuotedAttribute = x =>
37
45
  .replace(/&/g, "&")
38
46
  .replace(/"/g, """)
39
47
 
40
- const isMetaNode = Symbol.for("bruh meta node")
41
- const isMetaTextNode = Symbol.for("bruh meta text node")
42
- const isMetaElement = Symbol.for("bruh meta element")
43
- const isMetaRawString = Symbol.for("bruh meta raw string")
48
+ // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
49
+ const attributesToString = attributes =>
50
+ Object.entries(attributes)
51
+ .map(([name, value]) =>
52
+ value === ""
53
+ ? ` ${name}`
54
+ : ` ${name}="${escapeForDoubleQuotedAttribute(value)}"`
55
+ ).join("")
56
+
57
+ //#endregion
44
58
 
45
59
  // A basic check for if a value is allowed as a meta node's child
46
60
  // It's responsible for quickly checking the type, not deep validation
47
- const isMetaNodeChild = x =>
61
+ const isMetaChild = x =>
48
62
  // meta nodes, reactives, and DOM nodes
49
63
  x?.[isMetaNode] ||
50
64
  x?.[isMetaRawString] ||
@@ -57,8 +71,11 @@ const isMetaNodeChild = x =>
57
71
  // Everything else can be a child when stringified
58
72
 
59
73
 
60
- // Meta Nodes
74
+ //#region Meta Nodes that act like lightweight rendering-oriented DOM nodes
61
75
 
76
+ // Text nodes have no individual HTML representation
77
+ // We emulate this with a custom element <bruh-textnode> with an inline style reset
78
+ // These elements can be hydrated very quickly and even be marked with a tag
62
79
  export class MetaTextNode {
63
80
  [isMetaNode] = true;
64
81
  [isMetaTextNode] = true
@@ -71,20 +88,22 @@ export class MetaTextNode {
71
88
  }
72
89
 
73
90
  toString() {
74
- return `<bruh-textnode style="all:unset;display:inline"${
75
- this.tag
76
- ? ` data-tag="${escapeForDoubleQuotedAttribute(this.tag)}"`
77
- : ""
78
- }>${ escapeForElement(this.textContent) }</bruh-textnode>`
91
+ const tag = this.tag
92
+ ? ` tag="${escapeForDoubleQuotedAttribute(this.tag)}"`
93
+ : ""
94
+ return `<bruh-textnode style="all:unset;display:inline"${tag}>${
95
+ escapeForElement(this.textContent)
96
+ }</bruh-textnode>`
79
97
  }
80
98
 
81
- setTag(tag = "") {
99
+ setTag(tag) {
82
100
  this.tag = tag
83
101
 
84
102
  return this
85
103
  }
86
104
  }
87
105
 
106
+ // A light model of an element
88
107
  export class MetaElement {
89
108
  [isMetaNode] = true;
90
109
  [isMetaElement] = true
@@ -98,157 +117,160 @@ export class MetaElement {
98
117
  }
99
118
 
100
119
  toString() {
101
- // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
102
- const attributes =
103
- Object.entries(this.attributes)
104
- .map(([name, value]) =>
105
- value === ""
106
- ? ` ${name}`
107
- : ` ${name}="${escapeForDoubleQuotedAttribute(value)}"`
108
- ).join("")
120
+ const attributes = attributesToString(this.attributes)
109
121
  // https://html.spec.whatwg.org/multipage/syntax.html#syntax-start-tag
110
122
  const startTag = `<${this.name}${attributes}>`
111
-
112
123
  if (isVoidElement(this.name))
113
124
  return startTag
114
125
 
115
- const contents =
116
- this.children
117
- .flat(Infinity)
118
- .map(child => {
119
- return (child[isMetaNode] || child[isMetaRawString])
120
- ? child.toString()
121
- : escapeForElement(child)
122
- }).join("")
126
+ const contents = this.children
127
+ .flat(Infinity)
128
+ .map(child =>
129
+ (child[isMetaNode] || child[isMetaRawString])
130
+ ? child.toString()
131
+ : escapeForElement(child)
132
+ )
133
+ .join("")
123
134
  // https://html.spec.whatwg.org/multipage/syntax.html#end-tags
124
135
  const endTag = `</${this.name}>`
125
136
  return startTag + contents + endTag
126
137
  }
127
-
128
- addAttributes(attributes = {}) {
129
- Object.assign(this.attributes, attributes)
130
-
131
- return this
132
- }
133
-
134
- addDataAttributes(dataAttributes = {}) {
135
- Object.entries(dataAttributes)
136
- .forEach(([name, value]) => {
137
- // https://html.spec.whatwg.org/multipage/dom.html#dom-domstringmap-setitem
138
- const skewered = name.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)
139
- this.attributes[`data-${skewered}`] = value
140
- })
141
-
142
- return this
143
- }
144
-
145
- addStyles(styles = {}) {
146
- // Doesn't support proper escaping
147
- // https://www.w3.org/TR/css-syntax-3/#ref-for-parse-a-list-of-declarations%E2%91%A0
148
- // https://www.w3.org/TR/css-syntax-3/#typedef-ident-token
149
- const currentStyles = Object.fromEntries(
150
- (this.attributes.style || "")
151
- .split(";").filter(s => s.length)
152
- .map(declaration => declaration.split(":").map(s => s.trim()))
153
- )
154
-
155
- Object.assign(currentStyles, styles)
156
-
157
- this.attributes.style =
158
- Object.entries(currentStyles)
159
- .map(([property, value]) => `${property}:${value}`)
160
- .join(";")
161
-
162
- return this
163
- }
164
-
165
- toggleClasses(classes = {}) {
166
- // Doesn't support proper escaping
167
- // https://html.spec.whatwg.org/multipage/dom.html#global-attributes:classes-2
168
- const classList = new Set(
169
- (this.attributes.class || "")
170
- .split(/\s+/).filter(s => s.length)
171
- )
172
-
173
- Object.entries(classes)
174
- .forEach(([name, value]) => {
175
- if (value)
176
- classList.add(name)
177
- else
178
- classList.delete(name)
179
- })
180
-
181
- this.attributes.class = Array.from(classList).join(" ")
182
-
183
- return this
184
- }
185
138
  }
186
139
 
187
- export class MetaRawString {
140
+ // Raw strings can be meta element children, where they bypass string escaping
141
+ // This should be avoided in general, but is needed for unsupported HTML features
142
+ export class MetaRawString extends String {
188
143
  [isMetaRawString] = true
189
144
 
190
- string
191
-
192
145
  constructor(string) {
193
- this.string = string
146
+ super(string)
194
147
  }
148
+ }
195
149
 
196
- toString() {
197
- return this.string
198
- }
150
+ //#endregion
151
+
152
+ //#region Meta element helper functions e.g. applyAttributes()
153
+
154
+ // Merge style rules with an object
155
+ export const applyStyles = (element, styles) => {
156
+ // Doesn't support proper escaping
157
+ // https://www.w3.org/TR/css-syntax-3/#ref-for-parse-a-list-of-declarations%E2%91%A0
158
+ // https://www.w3.org/TR/css-syntax-3/#typedef-ident-token
159
+ const currentStyles = Object.fromEntries(
160
+ (element.attributes.style || "")
161
+ .split(";").filter(s => s.length)
162
+ .map(declaration => declaration.split(":").map(s => s.trim()))
163
+ )
164
+
165
+ Object.entries(styles)
166
+ .forEach(([property, value]) => {
167
+ if (value !== undefined)
168
+ currentStyles[property] = value
169
+ else
170
+ delete currentStyles[property]
171
+ })
172
+
173
+ element.attributes.style =
174
+ Object.entries(currentStyles)
175
+ .map(([property, value]) => `${property}:${value}`)
176
+ .join(";")
199
177
  }
200
178
 
179
+ // Merge classes with an object mapping from class names to booleans
180
+ export const applyClasses = (element, classes) => {
181
+ // Doesn't support proper escaping
182
+ // https://html.spec.whatwg.org/multipage/dom.html#global-attributes:classes-2
183
+ const currentClasses = new Set(
184
+ (element.attributes.class || "")
185
+ .split(/\s+/).filter(s => s.length)
186
+ )
187
+
188
+ Object.entries(classes)
189
+ .forEach(([name, value]) => {
190
+ if (value)
191
+ currentClasses.add(name)
192
+ else
193
+ currentClasses.delete(name)
194
+ })
195
+
196
+ element.attributes.class = [...currentClasses].join(" ")
197
+ }
198
+
199
+ // Merge attributes with an object
200
+ export const applyAttributes = (element, attributes) => {
201
+ Object.entries(attributes)
202
+ .forEach(([name, value]) => {
203
+ if (value !== undefined)
204
+ element.attributes[name] = value
205
+ else
206
+ delete element.attributes[name]
207
+ })
208
+ }
201
209
 
210
+ //#endregion
202
211
 
203
- // Convenience functions
212
+ //#region rawString(), t(), and e()
204
213
 
205
214
  export const rawString = string =>
206
215
  new MetaRawString(string)
207
216
 
208
- const createMetaTextNode = textContent =>
217
+ export const t = textContent =>
209
218
  new MetaTextNode(textContent)
210
219
 
211
- const createMetaElement = name => (...variadic) => {
212
- const meta = new MetaElement(name)
220
+ export const e = name => (...variadic) => {
221
+ const element = new MetaElement(name)
213
222
 
214
- // Implement optional attributes as first argument
215
- if (!isMetaNodeChild(variadic[0]))
216
- [meta.attributes, ...meta.children] = variadic
217
- else {
218
- meta.attributes = {}
219
- meta.children = variadic
223
+ // If there are no props
224
+ if (isMetaChild(variadic[0])) {
225
+ element.children.push(...variadic)
226
+ return element
220
227
  }
221
228
 
222
- return meta
223
- }
224
-
225
- // JSX integration
226
- const createMetaElementJSX = (nameOrComponent, attributesOrProps, ...children) => {
227
- // If we are making a html element
228
- // This is likely when the jsx tag name begins with a lowercase character
229
- if (typeof nameOrComponent == "string") {
230
- const meta = new MetaElement(nameOrComponent)
229
+ // If props exist as the first variadic argument
230
+ const [props, ...children] = variadic
231
231
 
232
- // These are attributes then, but they might be null/undefined
233
- meta.attributes = attributesOrProps || {}
234
- meta.children = children
232
+ // The bruh prop is reserved for future use
233
+ delete props.bruh
235
234
 
236
- return meta
235
+ // Apply overloaded props, if possible
236
+ if (typeof props.style === "object") {
237
+ applyStyles(element, props.style)
238
+ delete props.style
239
+ }
240
+ if (typeof props.class === "object") {
241
+ applyClasses(element, props.class)
242
+ delete props.class
237
243
  }
244
+ // The rest of the props are attributes
245
+ applyAttributes(element, props)
238
246
 
239
- // It must be a component, then
240
- // Bruh components are just functions that return meta elements
241
- // Due to JSX, this would mean a function with only one parameter - a "props" object
242
- // This object includes the all of the attributes and a "children" key
243
- return nameOrComponent( Object.assign({}, attributesOrProps, { children }) )
247
+ // Add the children to the element
248
+ element.children.push(...children)
249
+ return element
244
250
  }
245
251
 
246
- // These will be called with short names
247
- export {
248
- createMetaTextNode as t,
249
- createMetaElement as e,
250
- createMetaElementJSX as h
252
+ //#endregion
253
+
254
+ //#region JSX integration
255
+
256
+ // The function that jsx tags (except fragments) compile to
257
+ export const h = (nameOrComponent, props, ...children) => {
258
+ // If we are making an element, this is just a wrapper of e()
259
+ // This is likely when the JSX tag name begins with a lowercase character
260
+ if (typeof nameOrComponent === "string") {
261
+ const makeElement = e(nameOrComponent)
262
+ return props
263
+ ? makeElement(props, ...children)
264
+ : makeElement(...children)
265
+ }
266
+
267
+ // It must be a component, then, as bruh components are just functions
268
+ // Due to JSX, this would mean a function with only one parameter - props
269
+ // This object includes the all of the normal props and a "children" key
270
+ return nameOrComponent({ ...props, children })
251
271
  }
252
272
 
253
273
  // The JSX fragment is made into a bruh fragment (just an array)
254
274
  export const JSXFragment = ({ children }) => children
275
+
276
+ //#endregion
@@ -1,6 +1,4 @@
1
- /** @typedef { import("./index") } */
2
-
3
- const isReactive = Symbol.for("bruh reactive")
1
+ export const isReactive = Symbol.for("bruh reactive")
4
2
 
5
3
  // A super simple and performant reactive value implementation
6
4
  export class SimpleReactive {
@@ -81,7 +79,7 @@ export class FunctionalReactive {
81
79
  }
82
80
 
83
81
  set value(newValue) {
84
- // Only allow souce nodes to be directly updated
82
+ // Only allow source nodes to be directly updated
85
83
  if (this.#depth !== 0)
86
84
  return
87
85
 
@@ -31,20 +31,6 @@ export const createDestructable = (object, iterable) => {
31
31
  return destructable
32
32
  }
33
33
 
34
- // A function that acts like Maybe.from(x).ifExists(existsThen).ifEmpty(emptyThen)
35
- // Except we just use an array in place of a true Maybe type
36
- // This is useful for setting and removing reactive attributes
37
- export const maybeDo = (existsThen, emptyThen) => x => {
38
- if (Array.isArray(x)) {
39
- if (x.length)
40
- existsThen(x[0])
41
- else
42
- emptyThen()
43
- }
44
- else
45
- existsThen(x)
46
- }
47
-
48
34
  // Creates an object (as a Proxy) that acts as a function
49
35
  // So functionAsObject(f).property is equivalent to f("property")
50
36
  // This is can be useful when combined with destructuring syntax, e.g.:
@@ -1,113 +0,0 @@
1
- import { Reactive } from "../reactive"
2
-
3
- declare type MaybeReactiveRecord<T> = Record<string, T | Reactive<T>>
4
-
5
- declare const isReactive: unique symbol
6
- declare const isMetaNode: unique symbol
7
- declare const isMetaTextNode: unique symbol
8
- declare const isMetaElement: unique symbol
9
-
10
- declare type Stringifyable = { toString: () => string }
11
-
12
- declare type NonReactiveMetaNodeChild =
13
- | MetaNode
14
- | Node
15
- | string
16
- | number
17
- | Array<NonReactiveMetaNodeChild>
18
-
19
- export declare type MetaNodeChild =
20
- | NonReactiveMetaNodeChild
21
- | Reactive<NonReactiveMetaNodeChild>
22
-
23
- export declare type childrenToNodes = (children: Array<MetaNodeChild>) => Array<Node>
24
-
25
- export declare type MetaNode = {
26
- [isMetaNode]: true
27
- node: Node
28
- addProperties: (properties: object) => MetaNode
29
- }
30
-
31
- export declare class MetaTextNode implements MetaNode {
32
- constructor(value: string)
33
-
34
- [isMetaNode]: true
35
- [isMetaTextNode]: true
36
- node: Text
37
- addProperties: (properties: object) => MetaNode
38
- }
39
-
40
- export declare class MetaElement implements MetaNode {
41
- constructor(name: string, namespace?: string)
42
-
43
- static from: (element: Element) => MetaElement
44
-
45
- [isMetaNode]: true
46
- [isMetaElement]: true
47
- node: Element
48
- addProperties: (properties: object) => MetaNode
49
-
50
- addAttributes: (attributes: MaybeReactiveRecord<Stringifyable | void>) => MetaNode
51
- addDataAttributes: (dataAttributes: MaybeReactiveRecord<Stringifyable | void>) => MetaNode
52
- addStyles: (styles: MaybeReactiveRecord<Stringifyable | void>) => MetaNode
53
- toggleClasses: (classes: MaybeReactiveRecord<boolean>) => MetaNode
54
-
55
- before: (...children: Array<MetaNodeChild>) => MetaNode
56
- prepend: (...children: Array<MetaNodeChild>) => MetaNode
57
- append: (...children: Array<MetaNodeChild>) => MetaNode
58
- after: (...children: Array<MetaNodeChild>) => MetaNode
59
- replaceChildren: (...children: Array<MetaNodeChild>) => MetaNode
60
- replaceWith: (...children: Array<MetaNodeChild>) => MetaNode
61
- }
62
-
63
- export declare class MetaHTMLElement<Name extends keyof HTMLElementTagNameMap> extends MetaElement {
64
- constructor(name: Name, namespace?: "http://www.w3.org/1999/xhtml")
65
- node: HTMLElementTagNameMap[Name]
66
- }
67
- export declare class MetaSVGElement<Name extends keyof SVGElementTagNameMap> extends MetaElement {
68
- constructor(name: Name, namespace: "http://www.w3.org/2000/svg")
69
- node: SVGElementTagNameMap[Name]
70
- }
71
-
72
- export declare const hydrateTextNodes: () => Record<string, Text>
73
-
74
- declare const createMetaTextNode: (value: string) => MetaTextNode
75
-
76
- declare const createMetaElement: {
77
- <Name extends keyof HTMLElementTagNameMap>
78
- (name: Name, namespace?: "http://www.w3.org/1999/xhtml"): {
79
- ( ...children: Array<MetaNodeChild>): MetaHTMLElement<Name>
80
- (attributes: MaybeReactiveRecord<Stringifyable | void>, ...children: Array<MetaNodeChild>): MetaHTMLElement<Name>
81
- }
82
-
83
- <Name extends keyof SVGElementTagNameMap>
84
- (name: Name, namespace: "http://www.w3.org/2000/svg"): {
85
- ( ...children: Array<MetaNodeChild>): MetaSVGElement<Name>
86
- (attributes: MaybeReactiveRecord<Stringifyable | void>, ...children: Array<MetaNodeChild>): MetaSVGElement<Name>
87
- }
88
-
89
- (name: string, namespace?: string): {
90
- ( ...children: Array<MetaNodeChild>): MetaNode
91
- (attributes: MaybeReactiveRecord<Stringifyable | void>, ...children: Array<MetaNodeChild>): MetaNode
92
- }
93
- }
94
-
95
- declare const createMetaElementJSX: {
96
- <Name extends keyof HTMLElementTagNameMap>
97
- (name: Name, attributes: MaybeReactiveRecord<Stringifyable | void>, ...children: Array<MetaNodeChild>): MetaHTMLElement<Name>
98
-
99
- <Props, Child, ReturnType>
100
- (
101
- component: (propsAndChildren: Props & { children: Array<Child> }) => ReturnType,
102
- props: Props,
103
- ...children: Array<Child>
104
- ): ReturnType
105
- }
106
-
107
- export {
108
- createMetaTextNode as t,
109
- createMetaElement as e,
110
- createMetaElementJSX as h
111
- }
112
-
113
- declare const JSXFragment: <Child>(argument: { children: Array<Child> }) => Array<Child>
@@ -1,36 +0,0 @@
1
- declare const isReactive: unique symbol
2
-
3
- export declare type Reactive<T> = {
4
- [isReactive]: true
5
- value: T
6
- addReaction: (reaction: Function) => Function
7
- }
8
-
9
- export declare class SimpleReactive<T> implements Reactive<T> {
10
- constructor(value: T)
11
-
12
- [isReactive]: true
13
- get value(): T
14
- set value(newValue: T)
15
- addReaction(reaction: Function): () => boolean
16
- }
17
-
18
- export declare class FunctionalReactive<T> implements Reactive<T> {
19
- constructor(value: T)
20
- constructor(dependencies: Array<FunctionalReactive<unknown>>, f: () => T)
21
-
22
- [isReactive]: true
23
- get value(): T
24
- set value(newValue: T)
25
- addReaction(reaction: Function): () => boolean
26
- }
27
-
28
- export declare const r: {
29
- <T>(value: T): FunctionalReactive<T>
30
- <T>(dependencies: Array<FunctionalReactive<unknown>>, f: () => T): FunctionalReactive<T>
31
- }
32
-
33
- export declare const reactiveDo: {
34
- <T>(reactive: Reactive<T>, f: (value: T) => unknown): Function
35
- <T>(value: T, f: (value: T) => unknown): void
36
- }