nbhi 0.5.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/LICENSE +21 -0
- package/README.md +264 -0
- package/dist/index.js +2 -0
- package/dist/onceVisible.js +1 -0
- package/dist/update.js +2 -0
- package/dist/validate.js +1 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Roy Niels
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# What?
|
|
2
|
+
|
|
3
|
+
**HTML includes and reactive rendering, all without any build tools**
|
|
4
|
+
|
|
5
|
+
d--b is a simple **4KB** library that allows you to include html files into other html files and turns them into [web components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components). Lazy initialization of content (when entering the viewport) and a one line call to update/hydrate content in your html pages. No build tools required, no `package.json` needed.
|
|
6
|
+
|
|
7
|
+
# Why?
|
|
8
|
+
|
|
9
|
+
d--b is developed to create real-time data driven web sites. It has lower complexity than typical SPA applications but still offers more interactivy and flexibility than server side rendered web sites. _It works well with serverless application and a pub/sub data model_.
|
|
10
|
+
|
|
11
|
+
d--b is designed to be composable with other packages to build web sites. It takes the Lego approach, you select the packages for your needs, no lock-in into a single framework with a potential steep learning curve. d--b is build on standard browser supported technologies which means that you only need to read MDN as the source of knowledge.
|
|
12
|
+
|
|
13
|
+
d--b minimizes abstractions, just standard HTML, CSS and Javascript. Migrating from a SPA to d--b's multi page application (MPA) approach should mean you can do this one page at the time not having to migrate your whole system at once and pray that everything keeps working.
|
|
14
|
+
|
|
15
|
+
# Benefits
|
|
16
|
+
|
|
17
|
+
- Buildless web applications (minification is still possible of course) 👍
|
|
18
|
+
- Lightweight: **~4KB** 👍
|
|
19
|
+
- Benefit from standard web component encapsulation 👍
|
|
20
|
+
- Lazy load page content, when about to enter the viewport 👍
|
|
21
|
+
- Just standard web technologies, little abstractions 👍
|
|
22
|
+
- Composable with other packages
|
|
23
|
+
- Support for web component form fields out of the box 👍
|
|
24
|
+
- Multi Page Application (MPA) design
|
|
25
|
+
- The standard method how the web communicates between server and client 👍
|
|
26
|
+
- Normal native routing 👍
|
|
27
|
+
- Better SEO 👍
|
|
28
|
+
- Each page loads what it needs and no more 👍
|
|
29
|
+
- Fast initial draw 👍
|
|
30
|
+
|
|
31
|
+
# Examples
|
|
32
|
+
|
|
33
|
+
Please look in the **examples.html** for working examples.
|
|
34
|
+
|
|
35
|
+
# How?
|
|
36
|
+
|
|
37
|
+
## Templates
|
|
38
|
+
|
|
39
|
+
Templates let you build complex systems and reuse components accross multiple pages, you can define templates in separate files or use them inline as a one-of if you just need the benefits of web components.
|
|
40
|
+
|
|
41
|
+
### External templates
|
|
42
|
+
|
|
43
|
+
index.html
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
|
|
47
|
+
<html>
|
|
48
|
+
<body>
|
|
49
|
+
<my-component>Overwritten text</my-component>
|
|
50
|
+
|
|
51
|
+
<script type="module">
|
|
52
|
+
import initialize from '/d--b.js';
|
|
53
|
+
await initialize(); // await if loading external templates
|
|
54
|
+
</script>
|
|
55
|
+
</body>
|
|
56
|
+
</html>
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
/components/my-component.html
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
|
|
64
|
+
<div>
|
|
65
|
+
<slot>Default text</slot>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<style>/* Scoped to the web component */</style>
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Internal templates
|
|
73
|
+
|
|
74
|
+
index.html
|
|
75
|
+
|
|
76
|
+
```html
|
|
77
|
+
|
|
78
|
+
<html>
|
|
79
|
+
<body>
|
|
80
|
+
<template id="my-component">
|
|
81
|
+
<slot>Default</slot>
|
|
82
|
+
<style>/* Scoped to the web component */</style>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<my-component>Overwrite</my-component>
|
|
86
|
+
|
|
87
|
+
<script type="module">
|
|
88
|
+
import initialize from '/d--b.js';
|
|
89
|
+
initialize(); // No await needed for internal templates
|
|
90
|
+
</script>
|
|
91
|
+
</body>
|
|
92
|
+
</html>
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
_Note: When you use an internal `<template>` you need to define an id, which will be the name of the tag._
|
|
97
|
+
|
|
98
|
+
## Options
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Options for initiating an instance
|
|
104
|
+
* @param {string} [prefix=db]
|
|
105
|
+
* @param {string} [source=/components]
|
|
106
|
+
**/
|
|
107
|
+
|
|
108
|
+
import initialize from 'd--b.js'
|
|
109
|
+
initialize({
|
|
110
|
+
// Prefix to use, ie <db-input> or <db-nav>
|
|
111
|
+
prefix: 'db',
|
|
112
|
+
// Where to find external web components, if string it will be a director
|
|
113
|
+
// when using an object the key is the name (without prefix) of the component
|
|
114
|
+
// and the value the path to the web-component
|
|
115
|
+
directory: '/components',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Methods
|
|
121
|
+
|
|
122
|
+
### .setSlot(value, [slotName], [htmlSelector])
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
/**
|
|
126
|
+
* Update data in a <slot> tag
|
|
127
|
+
* @param {string} value - What you want to assign to a <slot>
|
|
128
|
+
* @param {string} [slotName] - If not given value is assigned to default <slot>
|
|
129
|
+
* @param {string} [htmlSelector] - Target a html tag in a complex <slot>
|
|
130
|
+
**/
|
|
131
|
+
|
|
132
|
+
const element = document.querySelector('my-component');
|
|
133
|
+
|
|
134
|
+
// To default slot
|
|
135
|
+
element.setSlot('Some text');
|
|
136
|
+
|
|
137
|
+
// To slot named title
|
|
138
|
+
element.setSlot('Some text', 'title');
|
|
139
|
+
|
|
140
|
+
// To slot named title and withing the <em> tag
|
|
141
|
+
element.setSlot('Some text', 'title', 'em');
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### .setChildren(data, [wrapperTag])
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
/**
|
|
149
|
+
* Updates one or more records in the child template
|
|
150
|
+
* @param {object|object[]} data - The data to use
|
|
151
|
+
**/
|
|
152
|
+
|
|
153
|
+
const element = document.querySelector('my-component');
|
|
154
|
+
|
|
155
|
+
// Update a single child, with data-slot named "title"
|
|
156
|
+
element.setChildren({ title: 'Some value' });
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
_Note: Child templates always need to extend an existing tag, like `<tr>`, `<option>`, `<div>`, etc. This is because some parents like `<tbody>`, `<select>` will not render regular non extended web components._
|
|
161
|
+
|
|
162
|
+
### .onceVisible(callback)
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
/**
|
|
166
|
+
* Runs one time when the web component comes into view
|
|
167
|
+
* @param {function} callback - Callback to run
|
|
168
|
+
**/
|
|
169
|
+
document.querySelector('my-component').onceVisible(element => {
|
|
170
|
+
subscribeToData('someCollection', data => element.setChildren(data));
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### .checkValidity() (Only if the webcomponent includes a form element)
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
/**
|
|
179
|
+
* Checks if the field is valid
|
|
180
|
+
* @returns boolean
|
|
181
|
+
**/
|
|
182
|
+
document.querySelector('my-form-field').checkValidity();
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### .validity (Only if the webcomponent includes a form element)
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
/**
|
|
190
|
+
* Get the standard validity object
|
|
191
|
+
* @returns ValidityState
|
|
192
|
+
**/
|
|
193
|
+
document.querySelector('my-form-field').validity;
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### .validationMessage() (Only if the webcomponent includes a form element)
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
/**
|
|
201
|
+
* Gets the message for an invalid field, handy for bespoke themes
|
|
202
|
+
* @returns string
|
|
203
|
+
**/
|
|
204
|
+
document.querySelector('my-form-field').validationMessage();
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Attributes
|
|
209
|
+
|
|
210
|
+
It is possible to set attributes on a web component instance tag like `<my-component>` and they will automatically be assigned to the correct nodes in the underlying `<template>`. Any changes you make to the `<my-component>` attributes will automatically update the underlying component data.
|
|
211
|
+
|
|
212
|
+
index.html
|
|
213
|
+
|
|
214
|
+
```html
|
|
215
|
+
|
|
216
|
+
<my-email data-id="1" value="me@me.com" readonly>User Email</my-email>
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
my-component.html
|
|
221
|
+
|
|
222
|
+
```html
|
|
223
|
+
<!--
|
|
224
|
+
Custom attributes like data-id need to be defined in the template, otherwise
|
|
225
|
+
d--b does not know where to assign the value
|
|
226
|
+
-->
|
|
227
|
+
<label data-id="">
|
|
228
|
+
<slot></slot>
|
|
229
|
+
<!-- d--b knows to add value and read only to the input field --->
|
|
230
|
+
<input type="email" placeholder="email">
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```js
|
|
236
|
+
|
|
237
|
+
// Calling from JS is also easier, you don't need to find the shadowRoot
|
|
238
|
+
const element = document.querySelector('my-component')
|
|
239
|
+
element.setAttribute('value', 'Some value');
|
|
240
|
+
element.disabled = true;
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
_Note: If there are multiple elements that can have an attribute that has been defined on the component instance it will assign it to all instances. You can assign attributes to the `<template>` tag but only if it extends an existing tag_
|
|
245
|
+
|
|
246
|
+
# FAQ
|
|
247
|
+
|
|
248
|
+
### How to pronounce d--b?
|
|
249
|
+
However you like.
|
|
250
|
+
|
|
251
|
+
### No build steps is nice, but I want to use TypeScript
|
|
252
|
+
If you want to use TS you have a couple of options. You can either add a build step, nothing is stopping you, the library supports it. If you want to stay buildless but want to add some sort of type checking and hinting you can consider using JSDoc, it is [supported](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#author) in TS as well.
|
|
253
|
+
|
|
254
|
+
### Creating interactivity is verbose with native js DOM manipulation
|
|
255
|
+
d--b is not designed to be a single solution for everything, if you want to make DOM manipulation for interactivity easier I would suggest using dedicated libraries for that. You can consider [Alpine.js](https://alpinejs.dev/) or [Umbrella.js](https://umbrellajs.com/) for example. d--b is designed with composibility in mind so you decide what to add.
|
|
256
|
+
|
|
257
|
+
### External npm packages require me the add build steps again
|
|
258
|
+
You could load them from a CDN if you want to stay 100% buildless, otherwise you could just add a simple minifier and bundler. This will stay very lightweight but probably is more suitable for production environments.
|
|
259
|
+
|
|
260
|
+
### Anybody using web component technology?
|
|
261
|
+
Just check out the source code of github.com or youtube.com.
|
|
262
|
+
|
|
263
|
+
### We are a team of multiple developers, using a framework ensures we write similar code
|
|
264
|
+
You are correct, when you choose a more opinionated framework it will most likely give more cohesive code overall when you work with multiple developers. However it also gives you lock-in. It is a trade-off that you have to make, depending on the team size and how agile your codebase needs to remain.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{getAttributeMapping as A,updateAttributes as E}from"./attributes.js";export default async({prefix:d="db",source:i="/components"}={})=>{const m=new Set,p=new Set;if(typeof i!="string"||!Object.keys(i).length)throw new Error("source option needs to be a string or an object");if(typeof d!="string"||d==="")throw new Error("prefix option needs to be a string of at least one character");const g=C();h(document.querySelector("body")),await y(document.querySelector("body"));function h(n,o){n.querySelectorAll("template").forEach(t=>{if(!t.id&&o&&(t.id=`${o}-child`),!t.id)throw console.log(t,o),new Error("<template> is missing a mandatory id field");h(t.content,t.id);const e=t.id;S({name:e,template:t})})}function y(n){const o=[...n.querySelectorAll(":not(:defined)")].filter(t=>{const e=t.localName;return t.nodeType===1&&e.includes("-")&&e.startsWith(d)}).map(async t=>{const e=t.localName;if(!p.has(e)){let l=function(u){const f=u.content.querySelector("script");return new Promise(async b=>{if(f){const x=new Blob([f.textContent],{type:"text/javascript"}),w=URL.createObjectURL(x);b((await import(w)).default),URL.revokeObjectURL(w)}else b({})})};p.add(e);const s=await a(e),r=document.createElement("template");r.content.append(...s.children);const c=await l(r);await y(r.content),h(r.content,e),S({name:e,template:r,script:c})}async function a(s){const r=s.split("-").slice(1).join("-"),c=typeof i=="string"?`${i}/${r}.html`:i[r],l=await fetch(c);if(!l.ok)throw new Error(`Could not find component @ ${c}`);const u=await l.text();return new DOMParser().parseFromString(u,"text/html").querySelector("body")}});return Promise.all(o)}function S({name:n,template:o,script:t}){if(m.has(n))return;m.add(n);const e=!!o.content.querySelector("input, select, textarea"),a=A(o.content);customElements.define(n,class extends HTMLElement{static observedAttributes=Object.keys(a);static formAssociated=e;#t=null;constructor(){super();const s=document.importNode(o.content,!0);this.#t=this.attachShadow({mode:"open"}),this.#t.adoptedStyleSheets=g,this.#t.appendChild(s),e&&(this.elementInternals=this.attachInternals(),this.formElement=this.#t.querySelector("input, select, textarea"))}connectedCallback(){typeof t=="function"&&t(this)}attributeChangedCallback(s,r,c){r!==c&&E({name:s,value:c,attributeMapping:a,root:this.#t})}})}function C(){return Array.from(document.styleSheets).filter(n=>n?.ownerNode?.dataset?.inherit==="true").map(n=>{const o=new CSSStyleSheet;try{const t=Array.from(n.cssRules).map(e=>e.cssText).join(`
|
|
2
|
+
`);o.replaceSync(t)}catch(t){console.error("Permission denied for sheet:",n.href)}return o})}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import o from"./element.js";export default(r,t)=>{const e=new IntersectionObserver(n=>{n[0].isIntersecting&&(t(),e.disconnect())});e.observe(o(r))};
|
package/dist/update.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{getAttributeMapping as $,updateAttributes as O}from"./attributes.js";import P from"./element.js";export default N=>{const f=P(N),d=new WeakMap,b=new WeakMap;let g=new Map,S=1,h;return l=>{if(j(l))throw new Error("Please convert date to string, ie. toISOString()");w(l)?q(l):m(l)?y(f,l):C(l)&&x(f,l);function j(t){return t?.getTime!==void 0}function m(t){return!w(t)&&!C(t)}function C(t){return!w(t)&&t!==null&&typeof t=="object"&&Object.keys(t).length}function w(t){return Array.isArray(t)}function x(t,r){Object.entries(r).forEach(([n,i])=>{if(n.startsWith("$")){const u=n.slice(1);if(t.shadowRoot)i===!1?t.removeAttribute(u):i===!0?t.setAttribute(u,""):t.setAttribute(u,i);else{b.has(t)||b.set(t,$(t));const A=b.get(t);O({name:u,value:i,attributeMapping:A,root:t})}}else y(t,i,n)})}function y(t,r,n){if(t.localName==="option")t.textContent=r;else if(t.localName==="tr"||t.localName==="li"||t.hasAttribute("child")&&!t.shadowRoot)if(t.hasAttribute("data-slot")&&t.dataset.slot===n)t.textContent=r;else{const i=t.querySelector(`[data-slot="${n}"]`);i&&(i.textContent=r)}else if(!n&&t.textContent!==r)t.textContent=r;else{let i=t.querySelector(`[slot="${n}"]`);i||(i=document.createElement("span"),i.setAttribute("slot",n),t.append(i)),i.textContent!==r&&(i.textContent=r)}}function q(t){const r=A();if(!h)throw new Error(`Cannot update ${N}, cannot find child element`);const n=new Map;t.forEach((e,a)=>{const p=u(e,a);let s=g.get(p);s||(s=h.cloneNode(!0),r.append(s)),s.cachedData!==e&&(C(e)?x(s,e):y(s,e),s.cachedData=e),n.set(p,s)}),g.forEach((e,a)=>{n.has(a)||e.remove()});let i;n.forEach(e=>{e!==i?.nextSibling&&r.insertBefore(e,i?i.nextSibling:r.firstChild),i=e}),g=n;function u(e,a){return e&&typeof e=="object"?(d.has(e)||d.set(e,S++),d.get(e)):`p:${e}:${a}`}function A(){const e=M();if(e.localName==="table")return a("tbody","tr");if(e.localName==="ol")return a("ol","li");if(e.localName==="ul")return a("ul","li");if(e.localName==="select")return a("select","option");if(e.querySelector("[child]")){const o=s(e.querySelector("[child]")).parentElement;return p("[child]",o),o.localName==="slot"&&o.hasAttribute("name")&&(h.slot=o.getAttribute("name")),o.localName==="slot"?f:o}throw new Error(`Could not find a parent to assign children to, valid
|
|
2
|
+
options are table, ul, ol, select or a structure with a child attribute`);function a(o,c){const E=s(o);return p(c,E),E}function p(o,c){h||(h=c.querySelector(o).cloneNode(!0),c.replaceChildren())}function s(o){const c=typeof o=="string"?e.querySelector(o):o;if(!c)throw new Error("Cannot update table, missing tbody");return c}function M(){return f.tagName.includes("-")?[...f.shadowRoot.children].find(o=>o.localName!=="script"&&o.localName!=="style"):f}}}}};
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import o from"./element.js";export default s=>{const e=o(s);if(e.elementInternals){let r=function(){return l.tooShort?`The text needs to be at least ${a} characters long`:l.tooLong?`The text needs to be at most ${i} characters long`:t.validationMessage?t.validationMessage:"The value is invalid"};const t=e.formElement,n=t.value,a=t.getAttribute("minlength"),i=t.getAttribute("maxlength"),l={valueMissing:t.validity.valueMissing,typeMismatch:t.validity.typeMismatch,patternMismatch:t.validity.patternMismatch,rangeUnderflow:t.validity.rangeUnderflow,rangeOverflow:t.validity.rangeOverflow,stepMismatch:t.validity.stepMismatch,badInput:t.validity.badInput,customError:t.validity.customError,tooShort:a?n.length<parseInt(a):!1,tooLong:i?n.length>parseInt(i):!1};return e.elementInternals.setFormValue(n),e.elementInternals.setValidity(l,r(),t),e.elementInternals}};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nbhi",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "No-Build HTML Includes (NBHI) is a simple library that allows you to include html files into other html files and turns them into [web components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components). Lazy initialization of content (when entering the viewport) and a one line call to update/hydrate content in your html pages. No build tools required, no `package.json` needed.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"web-components",
|
|
7
|
+
"html-includes",
|
|
8
|
+
"no-build",
|
|
9
|
+
"hydration",
|
|
10
|
+
"form-validation",
|
|
11
|
+
"lazy-loading",
|
|
12
|
+
"mpa",
|
|
13
|
+
"multi-page-application",
|
|
14
|
+
"composable"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/royniels/d--b#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/royniels/d--b/issues"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/royniels/d--b.git"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"./dist/**"
|
|
26
|
+
],
|
|
27
|
+
"exports": {
|
|
28
|
+
".": "./dist/index.js",
|
|
29
|
+
"./update.js": "./dist/update.js",
|
|
30
|
+
"./onceVisible": "./dist/onceVisible.js",
|
|
31
|
+
"./validate": "./dist/validate.js"
|
|
32
|
+
},
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"author": "Roy Niels",
|
|
35
|
+
"type": "module",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
38
|
+
}
|
|
39
|
+
}
|