bruh 1.8.1 → 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.
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "library",
11
11
  "modern"
12
12
  ],
13
- "version": "1.8.1",
13
+ "version": "1.10.0",
14
14
  "license": "MIT",
15
15
  "author": {
16
16
  "name": "Daniel Ethridge",
@@ -50,18 +50,10 @@
50
50
  "prepare": "npm run build"
51
51
  },
52
52
  "optionalDependencies": {
53
- "cac": "^6.7.3",
54
- "sharp": "^0.28.3"
53
+ "cac": "^6.7.11",
54
+ "sharp": "^0.29.2"
55
55
  },
56
56
  "devDependencies": {
57
- "vite": "^2.5.10"
58
- },
59
- "browserslist": [
60
- "ios_saf >= 12.4",
61
- "and_chr >= 88",
62
- "chrome >= 87",
63
- "safari >= 13.1",
64
- "edge >= 87",
65
- "firefox >= 84"
66
- ]
57
+ "vite": "^2.6.10"
58
+ }
67
59
  }
@@ -1,4 +1,7 @@
1
- import { picture, source, img } from "bruh/dom/html"
1
+ import { e } from "bruh/dom"
2
+ import { functionAsObject } from "bruh/util"
3
+ const { picture, source, img } = functionAsObject(e)
4
+
2
5
  import { readFile } from "fs/promises"
3
6
 
4
7
  export default async options => {
@@ -1,49 +1,49 @@
1
1
  import { LiveFragment } from "./live-fragment.mjs"
2
- import { reactiveDo } from "../reactive/index.mjs"
3
- import { maybeDo } from "../util/index.mjs"
2
+ import { isReactive, reactiveDo } from "../reactive/index.mjs"
4
3
 
5
- const isReactive = Symbol.for("bruh reactive")
6
- const isMetaNode = Symbol.for("bruh meta node")
7
- const isMetaTextNode = Symbol.for("bruh meta text node")
8
- const isMetaElement = Symbol.for("bruh meta element")
4
+ //#region Bruh child functions e.g. bruhChildrenToNodes()
9
5
 
10
- // A basic check for if a value is allowed as a meta node's child
6
+ // A basic check for if a value is allowed as a child in bruh
11
7
  // It's responsible for quickly checking the type, not deep validation
12
- const isMetaNodeChild = x =>
13
- // meta nodes, reactives, and DOM nodes
14
- x?.[isMetaNode] ||
8
+ const isBruhChild = x =>
9
+ // Reactives and DOM nodes
15
10
  x?.[isReactive] ||
16
11
  x instanceof Node ||
17
12
  // Any array, just assume it contains valid children
18
13
  Array.isArray(x) ||
19
- // Everything else, as long as it isn't a function, can be a child when stringified
20
- typeof x !== "function"
21
-
22
- const toNode = x => {
23
- if (x[isMetaNode])
24
- return x.node
25
-
26
- if (x instanceof Node)
27
- return x
28
-
29
- return document.createTextNode(x)
30
- }
31
-
32
- export const childrenToNodes = children =>
14
+ // Allow nullish
15
+ x == null ||
16
+ // Disallow functions and objects
17
+ !(typeof x === "function" || typeof x === "object")
18
+ // Everything else can be a child when stringified
19
+
20
+ // Coerces input into a DOM node, if it isn't already one
21
+ const toNode = x =>
22
+ x instanceof Node
23
+ ? x
24
+ : document.createTextNode(x)
25
+
26
+ // Processes bruh children into an array of DOM nodes
27
+ // Reactive values are automatically replaced, so the output must be placed into a parent node
28
+ // before any top level (after flattening arrays) reactions run
29
+ export const bruhChildrenToNodes = (...children) =>
33
30
  children
34
31
  .flat(Infinity)
35
32
  .flatMap(child => {
33
+ // Non-reactive values are untouched
36
34
  if (!child[isReactive])
37
35
  return [toNode(child)]
38
36
 
37
+ // Reactive arrays become live fragments with auto-swapped children
39
38
  if (Array.isArray(child.value)) {
40
39
  const liveFragment = new LiveFragment()
41
40
  child.addReaction(() => {
42
- liveFragment.replaceChildren(...childrenToNodes(child.value))
41
+ liveFragment.replaceChildren(...bruhChildrenToNodes(...child.value))
43
42
  })
44
- return [liveFragment.startMarker, ...childrenToNodes(child.value), liveFragment.endMarker]
43
+ return [liveFragment.startMarker, ...bruhChildrenToNodes(...child.value), liveFragment.endMarker]
45
44
  }
46
45
 
46
+ // Reactive values become auto-swapped DOM nodes
47
47
  let node = toNode(child.value)
48
48
  child.addReaction(() => {
49
49
  const oldNode = node
@@ -53,109 +53,129 @@ export const childrenToNodes = children =>
53
53
  return [node]
54
54
  })
55
55
 
56
+ //#endregion
56
57
 
58
+ //#region Reactive-aware element helper functions e.g. applyAttributes()
57
59
 
58
- // Meta Nodes
59
-
60
- export class MetaTextNode {
61
- constructor(textContent) {
62
- this[isMetaNode] =
63
- this[isMetaTextNode] = true
64
-
65
- if (textContent[isReactive]) {
66
- this.node = document.createTextNode(textContent.value)
67
- textContent.addReaction(() => {
68
- this.node.textContent = textContent.value
69
- })
70
- }
71
- else {
72
- this.node = document.createTextNode(textContent)
73
- }
74
- }
60
+ // Style attribute rules from an object with
61
+ // potentially reactive and/or undefined values
62
+ export const applyStyles = (element, styles) => {
63
+ for (const property in styles)
64
+ reactiveDo(styles[property], value => {
65
+ if (value !== undefined)
66
+ element.style.setProperty (property, value)
67
+ else
68
+ element.style.removeProperty(property)
69
+ })
70
+ }
75
71
 
76
- addProperties(properties = {}) {
77
- Object.assign(this.node, properties)
72
+ // Class list from an object mapping from
73
+ // class names to potentially reactive booleans
74
+ export const applyClasses = (element, classes) => {
75
+ for (const name in classes)
76
+ reactiveDo(classes[name], value => {
77
+ element.classList.toggle(name, value)
78
+ })
79
+ }
78
80
 
79
- return this
80
- }
81
+ // Attributes from an object with
82
+ // potentially reactive and/or undefined values
83
+ export const applyAttributes = (element, attributes) => {
84
+ for (const name in attributes)
85
+ reactiveDo(attributes[name], value => {
86
+ if (value !== undefined)
87
+ element.setAttribute (name, value)
88
+ else
89
+ element.removeAttribute(name)
90
+ })
81
91
  }
82
92
 
83
- export class MetaElement {
84
- constructor(name, namespace) {
85
- this[isMetaNode] =
86
- this[isMetaElement] = true
93
+ //#endregion
87
94
 
88
- this.node =
89
- namespace
90
- ? document.createElementNS(namespace, name)
91
- : document.createElement ( name)
92
- }
95
+ //#region t() for text nodes and e() for element nodes
93
96
 
94
- static from(element) {
95
- const result = new this("div")
96
- result.node = element
97
- return result
98
- }
97
+ // Text nodes
98
+ export const t = textContent => {
99
+ // Non-reactive values are just text nodes
100
+ if (!textContent[isReactive])
101
+ return document.createTextNode(textContent)
99
102
 
100
- addProperties(properties = {}) {
101
- Object.assign(this.node, properties)
103
+ // Reactive values auto-update the node's text content
104
+ const node = document.createTextNode(textContent.value)
105
+ textContent.addReaction(() => {
106
+ node.textContent = textContent.value
107
+ })
108
+ return node
109
+ }
102
110
 
103
- return this
111
+ // Elements
112
+ export const e = name => (...variadic) => {
113
+ // If there are no props
114
+ if (isBruhChild(variadic[0])) {
115
+ const element = document.createElement(name)
116
+ element.append(...bruhChildrenToNodes(...variadic))
117
+ return element
104
118
  }
105
119
 
106
- addAttributes(attributes = {}) {
107
- for (const name in attributes)
108
- reactiveDo(attributes[name],
109
- maybeDo(
110
- value => this.node.setAttribute (name, value),
111
- () => this.node.removeAttribute(name)
112
- )
113
- )
120
+ // If props exist as the first variadic argument
121
+ const [props, ...children] = variadic
114
122
 
115
- return this
116
- }
123
+ // Extract explicit options from the bruh prop
124
+ const { namespace } = props.bruh ?? {}
125
+ delete props.bruh
117
126
 
118
- addDataAttributes(dataAttributes = {}) {
119
- for (const name in dataAttributes)
120
- reactiveDo(dataAttributes[name],
121
- maybeDo(
122
- value => this.node.dataset[name] = value,
123
- () => delete this.node.dataset[name]
124
- )
125
- )
127
+ // Make an element with optional namespace
128
+ const element =
129
+ namespace
130
+ ? document.createElementNS(namespace, name)
131
+ : document.createElement ( name)
126
132
 
127
- return this
133
+ // Apply overloaded props, if possible
134
+ if (typeof props.style === "object") {
135
+ applyStyles(element, props.style)
136
+ delete props.style
128
137
  }
129
-
130
- before(...xs) {
131
- this.node.before(...childrenToNodes(xs))
138
+ if (typeof props.class === "object") {
139
+ applyClasses(element, props.class)
140
+ delete props.class
132
141
  }
142
+ // The rest of the props are attributes
143
+ applyAttributes(element, props)
133
144
 
134
- prepend(...xs) {
135
- this.node.prepend(...childrenToNodes(xs))
136
- }
145
+ // Add the children to the element
146
+ element.append(...bruhChildrenToNodes(...children))
147
+ return element
148
+ }
137
149
 
138
- append(...xs) {
139
- this.node.append(...childrenToNodes(xs))
140
- }
150
+ //#endregion
141
151
 
142
- after(...xs) {
143
- this.node.after(...childrenToNodes(xs))
144
- }
152
+ //#region JSX integration
145
153
 
146
- replaceChildren(...xs) {
147
- this.node.replaceChildren(...childrenToNodes(xs))
154
+ // The function that jsx tags (except fragments) compile to
155
+ export const h = (nameOrComponent, props, ...children) => {
156
+ // If we are making an element, this is just a wrapper of e()
157
+ // This is likely when the JSX tag name begins with a lowercase character
158
+ if (typeof nameOrComponent === "string") {
159
+ const makeElement = e(nameOrComponent)
160
+ return props
161
+ ? makeElement(props, ...children)
162
+ : makeElement(...children)
148
163
  }
149
164
 
150
- replaceWith(...xs) {
151
- this.node.replaceWith(...childrenToNodes(xs))
152
- }
165
+ // It must be a component, then, as bruh components are just functions
166
+ // Due to JSX, this would mean a function with only one parameter - props
167
+ // This object includes the all of the normal props and a "children" key
168
+ return nameOrComponent({ ...props, children })
153
169
  }
154
170
 
171
+ // The JSX fragment is made into a bruh fragment (just an array)
172
+ export const JSXFragment = ({ children }) => children
173
+
174
+ //#endregion
155
175
 
156
176
 
157
- // Convenience functions
158
177
 
178
+ // Hydration of all bruh-textnode's from prerendered html
159
179
  export const hydrateTextNodes = () => {
160
180
  const tagged = {}
161
181
  const bruhTextNodes = document.getElementsByTagName("bruh-textnode")
@@ -163,61 +183,12 @@ export const hydrateTextNodes = () => {
163
183
  for (const bruhTextNode of bruhTextNodes) {
164
184
  const textNode = document.createTextNode(bruhTextNode.textContent)
165
185
 
166
- if (bruhTextNode.dataset.tag)
167
- tagged[bruhTextNode.dataset.tag] = textNode
186
+ const tag = bruhTextNode.getAttribute("tag")
187
+ if (tag)
188
+ tagged[tag] = textNode
168
189
 
169
190
  bruhTextNode.replaceWith(textNode)
170
191
  }
171
192
 
172
193
  return tagged
173
194
  }
174
-
175
- const createMetaTextNode = textContent =>
176
- new MetaTextNode(textContent)
177
-
178
- const createMetaElement = (name, namespace) => (...variadic) => {
179
- const meta = new MetaElement(name, namespace)
180
-
181
- // Implement optional attributes as first argument
182
- if (!isMetaNodeChild(variadic[0])) {
183
- const [attributes, ...children] = variadic
184
- meta.addAttributes(attributes)
185
- meta.append(children)
186
- }
187
- else {
188
- meta.append(variadic)
189
- }
190
-
191
- return meta
192
- }
193
-
194
- // JSX integration
195
- const createMetaElementJSX = (nameOrComponent, attributesOrProps, ...children) => {
196
- // If we are making a html element
197
- // This is likely when the jsx tag name begins with a lowercase character
198
- if (typeof nameOrComponent == "string") {
199
- const meta = new MetaElement(nameOrComponent)
200
-
201
- // These are attributes then, but they might be null/undefined
202
- meta.addAttributes(attributesOrProps || {})
203
- meta.append(children)
204
-
205
- return meta
206
- }
207
-
208
- // It must be a component, then
209
- // Bruh components are just functions that return meta elements
210
- // Due to JSX, this would mean a function with only one parameter - a "props" object
211
- // This object includes the all of the attributes and a "children" key
212
- return nameOrComponent( Object.assign({}, attributesOrProps, { children }) )
213
- }
214
-
215
- // These will be called with short names
216
- export {
217
- createMetaTextNode as t,
218
- createMetaElement as e,
219
- createMetaElementJSX as h
220
- }
221
-
222
- // The JSX fragment is made into a bruh fragment (just an array)
223
- export const JSXFragment = ({ children }) => children