id-dom 0.0.3 → 0.0.5
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/ADDITIONAL_TERMS.md +122 -0
- package/LICENSE +694 -0
- package/README.md +295 -0
- package/dist/index.cjs +218 -77
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +218 -77
- package/dist/index.js.map +3 -3
- package/dist/index.min.js +1 -1
- package/package.json +5 -3
- package/robots.txt +79 -0
- package/LICENSE.md +0 -285
- package/Readme.md +0 -255
package/README.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# id-dom
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/id-dom)
|
|
4
|
+
[](https://www.npmjs.com/package/id-dom)
|
|
5
|
+
[](https://bundlephobia.com/package/id-dom)
|
|
6
|
+
[](https://github.com/iWhatty/id-dom/blob/main/LICENSE)
|
|
7
|
+
[](https://github.com/iWhatty/id-dom)
|
|
8
|
+
|
|
9
|
+
Deterministic DOM element getters by ID. Typed, tiny, modern. A small utility for grabbing DOM references safely by `id`, with predictable behavior.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Typed getters like `button('saveBtn')`, `input('nameInput')`, `svg('icon')`
|
|
14
|
+
- Strict or optional mode (`throw` vs `null`)
|
|
15
|
+
- Short optional alias via `.opt`
|
|
16
|
+
- Scoped lookups for `document`, `ShadowRoot`, `DocumentFragment`, or an `Element`
|
|
17
|
+
- Centralized error handling with `onError` and optional `warn`
|
|
18
|
+
- Zero dependencies
|
|
19
|
+
|
|
20
|
+
This is deliberately **not** a selector framework. It is a tiny, ID-first primitive for safe DOM wiring.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
pnpm add id-dom
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
Two import styles, same root, same behavior. Pick by preference:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
// Default-object style. Every typed helper lives under one namespace.
|
|
38
|
+
import dom from 'id-dom'
|
|
39
|
+
|
|
40
|
+
const saveBtn = dom.button('saveBtn')
|
|
41
|
+
saveBtn.addEventListener('click', save)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
// Named-import style, added in 0.0.5. Makes the dependency surface explicit
|
|
46
|
+
// in the import declaration. Tree-shaken identically by modern bundlers
|
|
47
|
+
// (esbuild, vite, rollup, webpack 5) since the package ships
|
|
48
|
+
// `sideEffects: false`.
|
|
49
|
+
import { button, input, div } from 'id-dom'
|
|
50
|
+
|
|
51
|
+
const saveBtn = button('saveBtn')
|
|
52
|
+
const email = input('email')
|
|
53
|
+
const panel = div('mainPanel')
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Optional access never throws for missing or wrong-type elements:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
const debug = dom.div.optional('debugPanel') // default-object style
|
|
60
|
+
debug?.append('hello')
|
|
61
|
+
|
|
62
|
+
import { canvas } from 'id-dom'
|
|
63
|
+
const maybeCanvas = canvas.opt('game') // named style
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## API
|
|
69
|
+
|
|
70
|
+
### Default export: `dom`
|
|
71
|
+
|
|
72
|
+
The default export is a scoped instance using `document` (when available) with **strict** behavior:
|
|
73
|
+
|
|
74
|
+
- missing element → **throws**
|
|
75
|
+
- wrong type or wrong tag → **throws**
|
|
76
|
+
- invalid input → **throws**
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
import dom from 'id-dom'
|
|
80
|
+
|
|
81
|
+
const name = dom.input('nameInput')
|
|
82
|
+
const submit = dom.button('submitBtn')
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `createDom(root, config?)`
|
|
86
|
+
|
|
87
|
+
Create a scoped instance that searches within a specific root:
|
|
88
|
+
|
|
89
|
+
- `document` → uses `getElementById`
|
|
90
|
+
- `ShadowRoot`, `DocumentFragment`, or `Element` → uses `querySelector(#id)` fallback
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
import { createDom } from 'id-dom'
|
|
94
|
+
|
|
95
|
+
const d = createDom(document, { mode: 'null', warn: true })
|
|
96
|
+
const sidebar = d.div('sidebar')
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Config:**
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
type DomMode = 'throw' | 'null'
|
|
103
|
+
|
|
104
|
+
{
|
|
105
|
+
mode?: DomMode
|
|
106
|
+
warn?: boolean
|
|
107
|
+
onError?: (err: Error, ctx: any) => void
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `byId(id, Type, config?)`
|
|
112
|
+
|
|
113
|
+
Generic typed lookup:
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
import { byId } from 'id-dom'
|
|
117
|
+
|
|
118
|
+
const btn = byId('saveBtn', HTMLButtonElement)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Optional variants:
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
const maybeBtn = byId.optional('saveBtn', HTMLButtonElement)
|
|
125
|
+
const maybeBtn2 = byId.opt('saveBtn', HTMLButtonElement)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Behavior:
|
|
129
|
+
|
|
130
|
+
- valid match → returns the element
|
|
131
|
+
- missing element → throws or returns `null`
|
|
132
|
+
- wrong type → throws or returns `null`
|
|
133
|
+
- invalid `id` → throws or returns `null`
|
|
134
|
+
- invalid `Type` → throws or returns `null`
|
|
135
|
+
|
|
136
|
+
### `tag(id, tagName, config?)`
|
|
137
|
+
|
|
138
|
+
Tag-based validation when constructor checks are not the right fit:
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
import { tag } from 'id-dom'
|
|
142
|
+
|
|
143
|
+
const main = tag('appMain', 'main')
|
|
144
|
+
const icon = tag('icon', 'svg', { root: container })
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Optional variants:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
const maybeMain = tag.optional('appMain', 'main')
|
|
151
|
+
const maybeMain2 = tag.opt('appMain', 'main')
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Behavior:
|
|
155
|
+
|
|
156
|
+
- valid tag match → returns the element
|
|
157
|
+
- missing element → throws or returns `null`
|
|
158
|
+
- wrong tag → throws or returns `null`
|
|
159
|
+
- invalid `id` → throws or returns `null`
|
|
160
|
+
- invalid `tagName` → throws or returns `null`
|
|
161
|
+
|
|
162
|
+
### Built-in getters
|
|
163
|
+
|
|
164
|
+
Typed getters available on `dom` and on any `createDom()` instance:
|
|
165
|
+
|
|
166
|
+
- `el(id)` → `HTMLElement`
|
|
167
|
+
- `input(id)` → `HTMLInputElement`
|
|
168
|
+
- `button(id)` → `HTMLButtonElement`
|
|
169
|
+
- `textarea(id)` → `HTMLTextAreaElement`
|
|
170
|
+
- `select(id)` → `HTMLSelectElement`
|
|
171
|
+
- `form(id)` → `HTMLFormElement`
|
|
172
|
+
- `div(id)` → `HTMLDivElement`
|
|
173
|
+
- `span(id)` → `HTMLSpanElement`
|
|
174
|
+
- `label(id)` → `HTMLLabelElement`
|
|
175
|
+
- `canvas(id)` → `HTMLCanvasElement`
|
|
176
|
+
- `template(id)` → `HTMLTemplateElement`
|
|
177
|
+
- `svg(id)` → `SVGSVGElement`
|
|
178
|
+
- `body(id)` → `HTMLBodyElement`
|
|
179
|
+
|
|
180
|
+
Each getter also has `.optional` and `.opt` variants:
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
dom.canvas.optional('game')
|
|
184
|
+
dom.canvas.opt('game')
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Common tag helpers:
|
|
188
|
+
|
|
189
|
+
- `main(id)` → validates `<main>`
|
|
190
|
+
- `section(id)` → validates `<section>`
|
|
191
|
+
- `small(id)` → validates `<small>`
|
|
192
|
+
|
|
193
|
+
Each also supports `.optional` and `.opt`.
|
|
194
|
+
|
|
195
|
+
### Error handling
|
|
196
|
+
|
|
197
|
+
**Throwing mode:**
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
import dom from 'id-dom'
|
|
201
|
+
|
|
202
|
+
dom.button('missing') // throws
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Null-returning mode:**
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
import { createDom } from 'id-dom'
|
|
209
|
+
|
|
210
|
+
const d = createDom(document, { mode: 'null' })
|
|
211
|
+
d.button('missing') // null
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Central reporting:**
|
|
215
|
+
|
|
216
|
+
```js
|
|
217
|
+
const d = createDom(document, {
|
|
218
|
+
mode: 'null',
|
|
219
|
+
onError: (err, ctx) => {
|
|
220
|
+
// sendToSentry({ err, ctx })
|
|
221
|
+
},
|
|
222
|
+
})
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Enable console warnings too:
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
createDom(document, { mode: 'null', warn: true })
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Notes
|
|
234
|
+
|
|
235
|
+
### Why id-first?
|
|
236
|
+
|
|
237
|
+
Using `getElementById` is fast, unambiguous, and easy to reason about. With typed getters, you immediately know whether you got a `HTMLButtonElement`, `HTMLInputElement`, `SVGSVGElement`, and so on.
|
|
238
|
+
|
|
239
|
+
When scoped roots do not support `getElementById`, id-dom falls back to `querySelector(#id)` and safely escapes edge-case IDs.
|
|
240
|
+
|
|
241
|
+
### Bundle-size note
|
|
242
|
+
|
|
243
|
+
The shared lookup machinery (validation, CSS-escape fallback, error policy, root resolution) is the bulk of the package, roughly 1.9 KB gzipped in a modern bundler. Importing 4 helpers vs 1 vs the full default object lands in the same ballpark. The named-import style is recommended for readability and explicit-surface clarity, not for size.
|
|
244
|
+
|
|
245
|
+
### Scoped roots
|
|
246
|
+
|
|
247
|
+
**Shadow DOM:**
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
import { createDom } from 'id-dom'
|
|
251
|
+
|
|
252
|
+
const host = document.querySelector('#widget')
|
|
253
|
+
const shadow = host.attachShadow({ mode: 'open' })
|
|
254
|
+
shadow.innerHTML = `<button id="shadowBtn">Click</button>`
|
|
255
|
+
|
|
256
|
+
const d = createDom(shadow)
|
|
257
|
+
const btn = d.button('shadowBtn')
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Element root:**
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
const container = document.querySelector('#settings-panel')
|
|
264
|
+
const d = createDom(container)
|
|
265
|
+
const input = d.input('emailInput')
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**SVG in scoped roots:**
|
|
269
|
+
|
|
270
|
+
```js
|
|
271
|
+
const container = document.querySelector('#icons')
|
|
272
|
+
const d = createDom(container)
|
|
273
|
+
const icon = d.svg('logoMark')
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Misc
|
|
277
|
+
|
|
278
|
+
- `el(id)` is specifically for `HTMLElement`, not every possible DOM `Element`.
|
|
279
|
+
- `body(id)` looks up a `<body>` **by ID**. This library stays ID-first on purpose.
|
|
280
|
+
- `tag()` can validate non-HTML tags too, such as `svg`, when used against supported scoped roots.
|
|
281
|
+
|
|
282
|
+
### Browser support
|
|
283
|
+
|
|
284
|
+
Modern browsers supporting:
|
|
285
|
+
|
|
286
|
+
- `getElementById`
|
|
287
|
+
- `querySelector`
|
|
288
|
+
|
|
289
|
+
`CSS.escape` is used when available. A safe internal fallback is included for environments such as some jsdom builds where it may be missing.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## License
|
|
294
|
+
|
|
295
|
+
Licensed under AGPL-3.0 with WATT3D Additional Terms. See [LICENSE](./LICENSE) and [ADDITIONAL_TERMS.md](./ADDITIONAL_TERMS.md). Commercial AI/model-training use requires compliance with those terms or a separate WATT3D license. © WATT3D.
|
package/dist/index.cjs
CHANGED
|
@@ -17,20 +17,48 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
18
|
var id_dom_exports = {};
|
|
19
19
|
__export(id_dom_exports, {
|
|
20
|
+
body: () => body,
|
|
21
|
+
button: () => button,
|
|
20
22
|
byId: () => byId,
|
|
23
|
+
canvas: () => canvas,
|
|
21
24
|
createDom: () => createDom,
|
|
22
25
|
default: () => id_dom_default,
|
|
23
|
-
|
|
26
|
+
div: () => div,
|
|
27
|
+
el: () => el,
|
|
28
|
+
form: () => form,
|
|
29
|
+
input: () => input,
|
|
30
|
+
label: () => label,
|
|
31
|
+
main: () => main,
|
|
32
|
+
section: () => section,
|
|
33
|
+
select: () => select,
|
|
34
|
+
small: () => small,
|
|
35
|
+
span: () => span,
|
|
36
|
+
svg: () => svg,
|
|
37
|
+
tag: () => tag,
|
|
38
|
+
template: () => template,
|
|
39
|
+
textarea: () => textarea
|
|
24
40
|
});
|
|
25
41
|
module.exports = __toCommonJS(id_dom_exports);
|
|
26
42
|
const DEFAULT_ROOT = typeof document !== "undefined" && document ? document : (
|
|
27
43
|
/** @type {any} */
|
|
28
44
|
null
|
|
29
45
|
);
|
|
46
|
+
const REASON = (
|
|
47
|
+
/** @type {const} */
|
|
48
|
+
{
|
|
49
|
+
INVALID_ID: "invalid-id",
|
|
50
|
+
INVALID_TYPE: "invalid-type",
|
|
51
|
+
INVALID_TAG: "invalid-tag",
|
|
52
|
+
MISSING: "missing",
|
|
53
|
+
WRONG_TYPE: "wrong-type",
|
|
54
|
+
WRONG_TAG: "wrong-tag"
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
const SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
58
|
+
const NEEDS_START_ESCAPE_RE = /^(?:\d|-\d)/;
|
|
30
59
|
function normalizeConfig(cfg) {
|
|
31
60
|
return {
|
|
32
61
|
mode: cfg?.mode ?? "throw",
|
|
33
|
-
// default strict
|
|
34
62
|
warn: cfg?.warn ?? false,
|
|
35
63
|
onError: typeof cfg?.onError === "function" ? cfg.onError : null,
|
|
36
64
|
root: cfg?.root ?? DEFAULT_ROOT
|
|
@@ -42,8 +70,6 @@ function hasGetElementById(v) {
|
|
|
42
70
|
function hasQuerySelector(v) {
|
|
43
71
|
return !!v && typeof v === "object" && typeof v.querySelector === "function";
|
|
44
72
|
}
|
|
45
|
-
const SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
46
|
-
const NEEDS_START_ESCAPE_RE = /^(?:\d|-\d)/;
|
|
47
73
|
function cssEscape(id) {
|
|
48
74
|
const s = String(id);
|
|
49
75
|
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
@@ -59,10 +85,13 @@ function cssEscape(id) {
|
|
|
59
85
|
cp >= 97 && cp <= 122 || // a-z
|
|
60
86
|
cp === 95 || // _
|
|
61
87
|
cp === 45;
|
|
62
|
-
const
|
|
88
|
+
const next = s.codePointAt(i + 1);
|
|
89
|
+
const startsWithDigit = cp >= 48 && cp <= 57;
|
|
90
|
+
const startsWithDashDigit = cp === 45 && s.length > 1 && next >= 48 && next <= 57;
|
|
91
|
+
const needsStartEscape = i === 0 && (startsWithDigit || startsWithDashDigit);
|
|
63
92
|
if (!needsStartEscape && (isAsciiSafe || cp >= 160)) {
|
|
64
93
|
out += ch;
|
|
65
|
-
} else if (
|
|
94
|
+
} else if (i === 0 && startsWithDashDigit) {
|
|
66
95
|
out += "\\-";
|
|
67
96
|
} else {
|
|
68
97
|
out += `\\${cp.toString(16).toUpperCase()} `;
|
|
@@ -71,16 +100,32 @@ function cssEscape(id) {
|
|
|
71
100
|
}
|
|
72
101
|
return out;
|
|
73
102
|
}
|
|
103
|
+
function isElementNode(v) {
|
|
104
|
+
if (!v || typeof v !== "object") return false;
|
|
105
|
+
if (typeof Element !== "undefined") return v instanceof Element;
|
|
106
|
+
return (
|
|
107
|
+
/** @type {any} */
|
|
108
|
+
v.nodeType === 1
|
|
109
|
+
);
|
|
110
|
+
}
|
|
74
111
|
function getById(root, id) {
|
|
75
112
|
if (!root) return null;
|
|
76
113
|
if (hasGetElementById(root)) return root.getElementById(id);
|
|
77
114
|
if (hasQuerySelector(root)) {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
return el instanceof HTMLElement ? el : null;
|
|
115
|
+
const el2 = root.querySelector(`#${cssEscape(id)}`);
|
|
116
|
+
return isElementNode(el2) ? el2 : null;
|
|
81
117
|
}
|
|
82
118
|
return null;
|
|
83
119
|
}
|
|
120
|
+
function isValidId(v) {
|
|
121
|
+
return typeof v === "string" && v.length > 0;
|
|
122
|
+
}
|
|
123
|
+
function isValidTagName(v) {
|
|
124
|
+
return typeof v === "string" && v.trim().length > 0;
|
|
125
|
+
}
|
|
126
|
+
function isConstructor(v) {
|
|
127
|
+
return typeof v === "function";
|
|
128
|
+
}
|
|
84
129
|
function fmtId(id) {
|
|
85
130
|
return id.startsWith("#") ? id : `#${id}`;
|
|
86
131
|
}
|
|
@@ -95,54 +140,114 @@ function handleLookupError(err, ctx, cfg) {
|
|
|
95
140
|
cfg.onError?.(err, ctx);
|
|
96
141
|
} catch {
|
|
97
142
|
}
|
|
98
|
-
if (cfg.warn) console.warn(err);
|
|
143
|
+
if (cfg.warn) console.warn(err, ctx);
|
|
99
144
|
if (cfg.mode === "throw") throw err;
|
|
100
145
|
return null;
|
|
101
146
|
}
|
|
102
|
-
function
|
|
147
|
+
function createCtx(id, root, reason, extra) {
|
|
148
|
+
return {
|
|
149
|
+
id,
|
|
150
|
+
root,
|
|
151
|
+
reason,
|
|
152
|
+
...extra || {}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function resolveLookup(config, spec) {
|
|
103
156
|
const cfg = normalizeConfig(config);
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
106
|
-
return handleLookupError(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
157
|
+
const inputFailure = spec.validateInput(cfg);
|
|
158
|
+
if (inputFailure) {
|
|
159
|
+
return handleLookupError(inputFailure.err, inputFailure.ctx, cfg);
|
|
160
|
+
}
|
|
161
|
+
const el2 = getById(cfg.root, spec.id);
|
|
162
|
+
if (!el2) {
|
|
163
|
+
const failure = spec.onMissing(cfg);
|
|
164
|
+
return handleLookupError(failure.err, failure.ctx, cfg);
|
|
165
|
+
}
|
|
166
|
+
if (!spec.matches(el2, cfg)) {
|
|
167
|
+
const failure = spec.onMismatch(el2, cfg);
|
|
168
|
+
return handleLookupError(failure.err, failure.ctx, cfg);
|
|
169
|
+
}
|
|
170
|
+
return (
|
|
171
|
+
/** @type {T} */
|
|
172
|
+
el2
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
function byId(id, Type, config) {
|
|
176
|
+
return resolveLookup(config, {
|
|
177
|
+
id,
|
|
178
|
+
validateInput(cfg) {
|
|
179
|
+
if (!isValidId(id)) {
|
|
180
|
+
return {
|
|
181
|
+
err: new Error("id-dom: invalid id (expected non-empty string)"),
|
|
182
|
+
ctx: createCtx(String(id), cfg.root, REASON.INVALID_ID, { Type })
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (!isConstructor(Type)) {
|
|
186
|
+
return {
|
|
187
|
+
err: new Error(`id-dom: invalid Type for ${fmtId(id)}`),
|
|
188
|
+
ctx: createCtx(id, cfg.root, REASON.INVALID_TYPE, { Type })
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
},
|
|
193
|
+
onMissing(cfg) {
|
|
194
|
+
return {
|
|
195
|
+
err: missingElError(id, Type.name),
|
|
196
|
+
ctx: createCtx(id, cfg.root, REASON.MISSING, { Type })
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
matches(el2) {
|
|
200
|
+
return el2 instanceof Type;
|
|
201
|
+
},
|
|
202
|
+
onMismatch(el2, cfg) {
|
|
203
|
+
const got = el2?.constructor?.name || typeof el2;
|
|
204
|
+
return {
|
|
205
|
+
err: wrongTypeError(id, Type.name, got),
|
|
206
|
+
ctx: createCtx(id, cfg.root, REASON.WRONG_TYPE, { Type, got })
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
});
|
|
121
210
|
}
|
|
122
211
|
byId.optional = function byIdOptional(id, Type, config) {
|
|
123
212
|
return byId(id, Type, { ...config, mode: "null" });
|
|
124
213
|
};
|
|
125
214
|
byId.opt = byId.optional;
|
|
126
215
|
function tag(id, tagName, config) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
216
|
+
return resolveLookup(config, {
|
|
217
|
+
id,
|
|
218
|
+
validateInput(cfg) {
|
|
219
|
+
if (!isValidId(id)) {
|
|
220
|
+
return {
|
|
221
|
+
err: new Error("id-dom: invalid id (expected non-empty string)"),
|
|
222
|
+
ctx: createCtx(String(id), cfg.root, REASON.INVALID_ID, { tagName })
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (!isValidTagName(tagName)) {
|
|
226
|
+
return {
|
|
227
|
+
err: new Error(`id-dom: invalid tagName for ${fmtId(id)}`),
|
|
228
|
+
ctx: createCtx(id, cfg.root, REASON.INVALID_TAG, { tagName })
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
},
|
|
233
|
+
onMissing(cfg) {
|
|
234
|
+
return {
|
|
235
|
+
err: missingElError(id, `<${tagName}>`),
|
|
236
|
+
ctx: createCtx(id, cfg.root, REASON.MISSING, { tagName })
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
matches(el2) {
|
|
240
|
+
return String(el2.tagName || "").toUpperCase() === String(tagName).toUpperCase();
|
|
241
|
+
},
|
|
242
|
+
onMismatch(el2, cfg) {
|
|
243
|
+
const expected = String(tagName).toUpperCase();
|
|
244
|
+
const got = String(el2.tagName || "").toUpperCase();
|
|
245
|
+
return {
|
|
246
|
+
err: wrongTypeError(id, `<${expected.toLowerCase()}>`, `<${got.toLowerCase()}>`),
|
|
247
|
+
ctx: createCtx(id, cfg.root, REASON.WRONG_TAG, { tagName, got })
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
});
|
|
146
251
|
}
|
|
147
252
|
tag.optional = function tagOptional(id, tagName, config) {
|
|
148
253
|
return tag(id, tagName, { ...config, mode: "null" });
|
|
@@ -162,7 +267,8 @@ const TYPE_HELPERS = (
|
|
|
162
267
|
label: typeof HTMLLabelElement !== "undefined" ? HTMLLabelElement : null,
|
|
163
268
|
canvas: typeof HTMLCanvasElement !== "undefined" ? HTMLCanvasElement : null,
|
|
164
269
|
template: typeof HTMLTemplateElement !== "undefined" ? HTMLTemplateElement : null,
|
|
165
|
-
svg: typeof SVGSVGElement !== "undefined" ? SVGSVGElement : null
|
|
270
|
+
svg: typeof SVGSVGElement !== "undefined" ? SVGSVGElement : null,
|
|
271
|
+
body: typeof HTMLBodyElement !== "undefined" ? HTMLBodyElement : null
|
|
166
272
|
}
|
|
167
273
|
);
|
|
168
274
|
const TAG_HELPERS = (
|
|
@@ -173,45 +279,80 @@ const TAG_HELPERS = (
|
|
|
173
279
|
small: "small"
|
|
174
280
|
}
|
|
175
281
|
);
|
|
282
|
+
function attachOptional(fn, optionalFn) {
|
|
283
|
+
if (typeof fn !== "function") {
|
|
284
|
+
throw new TypeError("id-dom: attachOptional expected fn to be a function");
|
|
285
|
+
}
|
|
286
|
+
if (typeof optionalFn !== "function") {
|
|
287
|
+
throw new TypeError("id-dom: attachOptional expected optionalFn to be a function");
|
|
288
|
+
}
|
|
289
|
+
fn.optional = optionalFn;
|
|
290
|
+
fn.opt = optionalFn;
|
|
291
|
+
return fn;
|
|
292
|
+
}
|
|
293
|
+
function makeTypedHelper(Type, base, baseNull) {
|
|
294
|
+
if (!Type) {
|
|
295
|
+
throw new TypeError("id-dom: makeTypedHelper received an invalid Type");
|
|
296
|
+
}
|
|
297
|
+
return attachOptional(
|
|
298
|
+
(id) => byId(id, Type, base),
|
|
299
|
+
(id) => byId(id, Type, baseNull)
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
function makeTagHelper(tagName, base, baseNull) {
|
|
303
|
+
if (!tagName) {
|
|
304
|
+
throw new TypeError("id-dom: makeTagHelper received an invalid tagName");
|
|
305
|
+
}
|
|
306
|
+
return attachOptional(
|
|
307
|
+
(id) => tag(id, tagName, base),
|
|
308
|
+
(id) => tag(id, tagName, baseNull)
|
|
309
|
+
);
|
|
310
|
+
}
|
|
176
311
|
function createDom(root, config) {
|
|
177
312
|
const base = normalizeConfig({ ...config, root });
|
|
178
313
|
const baseNull = { ...base, mode: "null" };
|
|
179
|
-
const api = {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
314
|
+
const api = {};
|
|
315
|
+
api.byId = attachOptional(
|
|
316
|
+
(id, Type) => byId(id, Type, base),
|
|
317
|
+
(id, Type) => byId(id, Type, baseNull)
|
|
318
|
+
);
|
|
319
|
+
api.tag = attachOptional(
|
|
320
|
+
(id, name) => tag(id, name, base),
|
|
321
|
+
(id, name) => tag(id, name, baseNull)
|
|
322
|
+
);
|
|
184
323
|
for (const [name, Type] of Object.entries(TYPE_HELPERS)) {
|
|
185
324
|
if (!Type) continue;
|
|
186
|
-
api[name] = (
|
|
325
|
+
api[name] = makeTypedHelper(Type, base, baseNull);
|
|
187
326
|
}
|
|
188
327
|
for (const [name, tagName] of Object.entries(TAG_HELPERS)) {
|
|
189
|
-
api[name] = (
|
|
190
|
-
}
|
|
191
|
-
api.byId.optional = (id, Type) => byId(id, Type, baseNull);
|
|
192
|
-
api.byId.opt = api.byId.optional;
|
|
193
|
-
api.tag.optional = (id, name) => tag(id, name, baseNull);
|
|
194
|
-
api.tag.opt = api.tag.optional;
|
|
195
|
-
for (const k of Object.keys(api)) {
|
|
196
|
-
const fn = api[k];
|
|
197
|
-
if (typeof fn !== "function") continue;
|
|
198
|
-
if (fn.optional) continue;
|
|
199
|
-
if (k in TAG_HELPERS) {
|
|
200
|
-
const tagName = TAG_HELPERS[k];
|
|
201
|
-
fn.optional = (id) => tag(id, tagName, baseNull);
|
|
202
|
-
fn.opt = fn.optional;
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
if (k in TYPE_HELPERS) {
|
|
206
|
-
const Type = TYPE_HELPERS[k];
|
|
207
|
-
if (Type) {
|
|
208
|
-
fn.optional = (id) => byId(id, Type, baseNull);
|
|
209
|
-
fn.opt = fn.optional;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
328
|
+
api[name] = makeTagHelper(tagName, base, baseNull);
|
|
212
329
|
}
|
|
213
330
|
return api;
|
|
214
331
|
}
|
|
332
|
+
const DEFAULT_BASE = normalizeConfig({ mode: "throw", root: DEFAULT_ROOT });
|
|
333
|
+
const DEFAULT_BASE_NULL = { ...DEFAULT_BASE, mode: "null" };
|
|
334
|
+
function defaultTypedHelper(Type) {
|
|
335
|
+
return Type ? makeTypedHelper(Type, DEFAULT_BASE, DEFAULT_BASE_NULL) : null;
|
|
336
|
+
}
|
|
337
|
+
function defaultTagHelper(tagName) {
|
|
338
|
+
return makeTagHelper(tagName, DEFAULT_BASE, DEFAULT_BASE_NULL);
|
|
339
|
+
}
|
|
340
|
+
const el = defaultTypedHelper(typeof HTMLElement !== "undefined" ? HTMLElement : null);
|
|
341
|
+
const input = defaultTypedHelper(typeof HTMLInputElement !== "undefined" ? HTMLInputElement : null);
|
|
342
|
+
const button = defaultTypedHelper(typeof HTMLButtonElement !== "undefined" ? HTMLButtonElement : null);
|
|
343
|
+
const textarea = defaultTypedHelper(typeof HTMLTextAreaElement !== "undefined" ? HTMLTextAreaElement : null);
|
|
344
|
+
const select = defaultTypedHelper(typeof HTMLSelectElement !== "undefined" ? HTMLSelectElement : null);
|
|
345
|
+
const form = defaultTypedHelper(typeof HTMLFormElement !== "undefined" ? HTMLFormElement : null);
|
|
346
|
+
const div = defaultTypedHelper(typeof HTMLDivElement !== "undefined" ? HTMLDivElement : null);
|
|
347
|
+
const span = defaultTypedHelper(typeof HTMLSpanElement !== "undefined" ? HTMLSpanElement : null);
|
|
348
|
+
const label = defaultTypedHelper(typeof HTMLLabelElement !== "undefined" ? HTMLLabelElement : null);
|
|
349
|
+
const canvas = defaultTypedHelper(typeof HTMLCanvasElement !== "undefined" ? HTMLCanvasElement : null);
|
|
350
|
+
const template = defaultTypedHelper(typeof HTMLTemplateElement !== "undefined" ? HTMLTemplateElement : null);
|
|
351
|
+
const svg = defaultTypedHelper(typeof SVGSVGElement !== "undefined" ? SVGSVGElement : null);
|
|
352
|
+
const body = defaultTypedHelper(typeof HTMLBodyElement !== "undefined" ? HTMLBodyElement : null);
|
|
353
|
+
const main = defaultTagHelper("main");
|
|
354
|
+
const section = defaultTagHelper("section");
|
|
355
|
+
const small = defaultTagHelper("small");
|
|
215
356
|
const dom = createDom(DEFAULT_ROOT, { mode: "throw" });
|
|
216
357
|
var id_dom_default = dom;
|
|
217
358
|
//# sourceMappingURL=index.cjs.map
|