decue 1.0.1 → 1.1.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/README.md +15 -1
- package/dist/decue.js +309 -172
- package/dist/decue.min.js +1 -1
- package/eslint.config.js +53 -0
- package/examples.html +29 -13
- package/npm.sh +1 -1
- package/package.json +17 -8
- package/src/decue.js +309 -172
- package/test/debug.test.js +201 -0
- package/test/decue.test.js +78 -0
- package/test/errors.test.js +212 -0
- package/test/eventHandlers.test.js +95 -0
- package/test/formAssociated.test.js +477 -0
- package/test/init.test.js +43 -0
- package/test/lifecycle.test.js +110 -0
- package/test/memory.test.js +283 -0
- package/test/piped.test.js +152 -0
- package/test/placeholders.test.js +396 -0
- package/test/predefined.test.js +131 -0
- package/test/scriptAttributes.test.js +464 -0
- package/test/slots.test.js +293 -0
- package/test/test-helpers.js +36 -0
- package/tsconfig.json +1 -1
- package/web-test-runner.config.mjs +30 -0
- package/serve.sh +0 -4
- package/test/decue-tests.js +0 -84
- package/test/index.html +0 -65
- package/test/util/util.js +0 -17
package/dist/decue.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var decue=function(){
|
|
1
|
+
var decue=function(){var r=("currentScript"in document&&document["currentScript"]?document["currentScript"].hasAttribute("debug"):false)||false;var n=("currentScript"in document&&document["currentScript"]?document["currentScript"].getAttribute("shadow"):undefined)||"none";if(!["open","closed","none"].includes(n)){throw new Error('Invalid default shadow DOM mode "'+n+'". Must be one of "open", "closed" or "none".')}var s=/{(\.?[a-zA-Z_0-9]+)((?:[.|][a-zA-Z_0-9]+)*)}/g;var l=function(e){return!(e==="shadow"||e.startsWith("decue-"))};var a=function(o){return function(e,t){var n=t.startsWith(".")?t.substring(1):undefined;var r=n&&typeof e[n]==="function";var i=c[t];if(!n&&!i){throw new Error('Global function "'+t+'" not found. Make sure to include it (not deferred) before this element is created.')}return n?r?e[n]():e[n]:i.apply(o,[e])}};var u=function(i,o,e){return e.replaceAll(s,function(e,t,n){var r=(t+n).replaceAll(/([^|])\./g,"$1|.").split("|");return o.hasAttribute(t)?r.slice(1).reduce(a(i),o.getAttribute(t)):t.startsWith(".")?r.reduce(a(i),o):e})};var d=function(e,t){var n=t[0];var r=t[1];var i=t[2];var o=u(n,e,r);if(n.nodeType===Node.TEXT_NODE){var a=n;if(o!==a.data){a.data=o}}else if(n.nodeType===Node.ELEMENT_NODE){var s=n;if(o!==s.getAttribute(i)){s.setAttribute(i,o)}}};var f=function(e,r){var t=document.createTreeWalker(e,4|1);while(t.nextNode()){var n=t.currentNode;if(n.nodeType===Node.TEXT_NODE){var i=n;var o;while((o=s.exec(i.data))!==null){r(i,null,o[1])}}else if(n.nodeType===Node.ELEMENT_NODE){var a=n;a.getAttributeNames().forEach(function(e){var t=a.getAttribute(e);var n;while((n=s.exec(t))!==null){r(a,e,n[1])}})}}};var h=function(r,e){e.querySelectorAll("slot[name]").forEach(function(e){var t=r.querySelectorAll(':scope > [slot="'+e.getAttribute("name")+'"]');var n=t.length>0?t:e.children;e.replaceWith.apply(e,n)});var t=e.querySelector("slot:not([name])");if(t){var n=Array.prototype.slice.call(r.childNodes).filter(function(e){return e.nodeType!==Node.ELEMENT_NODE||!e.hasAttribute("slot")});var i=n.length>0?n:t.children;t.replaceWith.apply(t,i)}};var b=function(r){return r.getAttributeNames().filter(function(e){return e.startsWith("decue-on:")}).map(function(e){return e.substring("decue-on:".length)}).map(function(e){var t=r.getAttribute("decue-on:"+e);var n=globalThis[t];if(!n){throw'Global handler function "'+t+'" for event "'+e+'" not found.'}r.addEventListener(e,n);return[e,n]})};var v=function(t,e){if(e){var n=e.filter(function(e){return e[0].ownerDocument!==undefined});if(n.length!==e.length){e=n}e.forEach(function(e){d(t,e)})}return e};var i=function(i,o,a,s,e){var u=function(){if(i&&console){console.log.apply(null,[o].concat(Array.prototype.slice.call(arguments)))}};var c=e;if(a){f(a.content,function(e,t,n){c.push(n)})}c=c.filter(l).filter(function(e,t,n){return n.indexOf(e)===t});globalThis.customElements.define(o,class extends HTMLElement{static get formAssociated(){return s}static get observedAttributes(){return c}constructor(){super();this._template=a||Array.prototype.slice.call(document.getElementsByTagName("template")).filter(function(e){return e.getAttribute("decue")===o}).concat([undefined])[0];if(!this._template){throw`Template for "${o}" not found. Make sure it comes in the DOM before any corresponding custom element.`}if(!s&&this._template.hasAttribute("formAssociated")){throw`Cannot declare a predefined custom element "${o}" as formAssociated. Move it from elements="..." to formAssociated="...".`}if(s){var n=this.attachInternals();var r;var i=this;Object.defineProperties(this,{internals:{value:n,writable:false},value:{get:function(){return r},set:function(e){r=e;n.setFormValue(r);i.checkValidity();i._boundNodes=v(i,i._boundNodes)}},name:{get:function(){return this.getAttribute("name")}},form:{get:function(){return n.form}},labels:{get:function(){return n.labels}},validity:{get:function(){return n.validity}},validationMessage:{get:function(){return n.validationMessage}},willValidate:{get:function(){return n.willValidate}},setFormValue:{value:function(e,t){n.setFormValue(r=e,t);i._boundNodes=v(i,i._boundNodes)},writable:false},setValidity:{value:n.setValidity.bind(n),writable:false},checkValidity:{value:function(){m(i,"checkvalidity");return n.checkValidity()},writable:false},reportValidity:{value:n.reportValidity.bind(n),writable:false}})}}connectedCallback(){this._shadow=this.getAttribute("shadow")||this._template.getAttribute("shadow")||n;if(!["open","closed","none"].includes(this._shadow)){throw`Invalid shadow DOM mode "${this._shadow}" for element "${o}". Must be one of "open", "closed" or "none".`}if(!this._shadowRoot){this._shadowRoot=this._shadow==="none"?this:this.attachShadow({mode:this._shadow,delegatesFocus:true});if(s){this.value=this.getAttribute("value");u("Making form-associated with value",this.value);if(!this.hasAttribute("tabindex")){this.tabIndex=0}}this._eventListeners=b(this);var r=this;var e=function(){u("Finalizing...");var e=r._template.content.cloneNode(true);if(r._shadow==="none"){h(r._shadowRoot,e);u("Slots initialized")}r._boundNodes=[];[e,r].forEach(function(e){f(e,function(e,t){r._boundNodes.push(e.nodeType==Node.ELEMENT_NODE?[e,e.getAttribute(t),t]:[e,e.data,undefined])})});var n=r.getAttributeNames().filter(l).filter(function(e){return!c.includes(e)});if(n.length>0&&r._boundNodes.length>0){if(typeof MutationObserver==="undefined"){console.info(`Decue: Element "${o}" has attributes (${n.join(" ")}) which are not declared in observedAttributes, but no MutationObserver is available. These attributes won't be observed.`)}else{r._observer=new MutationObserver(function(e){e.forEach(function(e){if(n.includes(e.attributeName)){var t=e.target.getAttribute(e.attributeName);u("Attribute observer hit",e.attributeName,t,"(unobserved attributes: ",n,")");r.attributeChangedCallback(e.attributeName,e.oldValue,t)}})});r._observer.observe(r,{attributeOldValue:true});r._decueMutationObserved=true;if(i){u("Observing attributes with MutationObserver",n);r.setAttribute("data-decue-mutation-observed-attributes",n.join(" "))}}}if(i&&c.length>0){u("Observing attributes with observedAttributes",c);r.setAttribute("data-decue-observed-attributes",c.join(" "))}r._shadowRoot.append(e);r._boundNodes.forEach(function(e){d(r,e)});m(r,"connect")};if(a||r.ownerDocument.readyState!="loading"){e()}else if(typeof MutationObserver==="undefined"){throw new Error(`No MutationObserver available, cannot use predefined elements`)}else{var t=new MutationObserver(function(){t.disconnect();e()});t.observe(r.parentElement,{childList:true})}}}attributeChangedCallback(e,t,n){if(t!==n){if(s&&e==="value"&&this.value!==n){this.value=n}this._boundNodes=v(this,this._boundNodes);m(this,"attributechange",{name:e,oldValue:t,newValue:n})}}disconnectedCallback(){m(this,"disconnect");if(this._observer){u("Disconnecting MutationObserver");this._observer.disconnect();delete this._observer}if(this._eventListeners){u("Removing event listeners");this._eventListeners.forEach(function(e){this.removeEventListener(e[0],e[1])},this);delete this._eventListeners}}adoptedCallback(){m(this,"adopt")}formAssociatedCallback(e){m(this,"formassociate",{form:e})}formDisabledCallback(e){m(this,"formdisable",{disabled:e})}formResetCallback(){m(this,"formreset")}formStateRestoreCallback(e,t){m(this,"formstaterestore",{state:e,mode:t})}})};var m=function(e,t,n){if(typeof CustomEvent!=="undefined"){e.dispatchEvent(new CustomEvent(t,{detail:n||{},bubbles:true}))}else{e.dispatchEvent(new Event(t,{detail:n||{},bubbles:true}))}};[{attr:"elements",formAssociated:false},{attr:"form-associated",formAssociated:true}].forEach(function(n){if(document.currentScript.hasAttribute(n.attr)){document.currentScript.getAttribute(n.attr).split(/\s+/).map(function(e){return e.split(/\[|]/)}).forEach(function([e,t]){i(r,e,null,n.formAssociated,t?t.split(","):[])})}});var c={};if(document.currentScript&&document.currentScript.hasAttribute("functions")){document.currentScript.getAttribute("functions").split(/\s+/).forEach(function(n){var e=n;var t=n;var r=n.indexOf(":");if(r>=0){e=n.substring(0,r);t=n.substring(r+1)}var i=t.split(".").reduce(function(e,t){if(e&&t in e){return e[t]}else{throw`Cannot find namespace "${t}" while resolving function "${n}".`}},globalThis);if(typeof i!=="function"){throw`Function "${n}" not found. Make sure to include it (not deferred) before Decue script.`}c[e]=i})}var t=function(e){var t=e.getAttribute("decue");if(t&&!globalThis.customElements.get(t)){i(r,t,e,e.hasAttribute("form-associated"),[])}};var e=function(){[...document.getElementsByTagName("template")].forEach(t);[...document.getElementsByTagName("object")].filter(function(e){return e.getAttribute("type")==="text/html"}).forEach(function(e){e.addEventListener("load",function(){if(e.contentDocument){[...e.contentDocument.getElementsByTagName("template")].forEach(t)}});if(e.contentDocument){[...e.contentDocument.getElementsByTagName("template")].forEach(t)}})};if(typeof globalThis!=="undefined"){globalThis.addEventListener("DOMContentLoaded",e)}else if(typeof window!=="undefined"){window.addEventListener("DOMContentLoaded",e)}return{processTemplate:t,defineElement:i,registeredFunctions:c}}();if(typeof globalThis!=="undefined"){globalThis.decue=decue}else if(typeof window!=="undefined"){window.decue=decue}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import js from '@eslint/js';
|
|
2
|
+
import compat from 'eslint-plugin-compat';
|
|
3
|
+
|
|
4
|
+
export default [
|
|
5
|
+
js.configs.recommended,
|
|
6
|
+
{
|
|
7
|
+
ignores: ['coverage/', 'dist/', 'node_modules/', '**/*.min.js']
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
files: ['src/**/*.js'],
|
|
11
|
+
languageOptions: {
|
|
12
|
+
ecmaVersion: 2015,
|
|
13
|
+
sourceType: 'script',
|
|
14
|
+
globals: {
|
|
15
|
+
// Browser globals
|
|
16
|
+
globalThis: 'readonly',
|
|
17
|
+
window: 'readonly',
|
|
18
|
+
document: 'readonly',
|
|
19
|
+
console: 'readonly',
|
|
20
|
+
setTimeout: 'readonly',
|
|
21
|
+
clearTimeout: 'readonly',
|
|
22
|
+
performance: 'readonly',
|
|
23
|
+
customElements: 'readonly',
|
|
24
|
+
HTMLElement: 'readonly',
|
|
25
|
+
HTMLTableElement: 'readonly',
|
|
26
|
+
HTMLTableRowElement: 'readonly',
|
|
27
|
+
HTMLTableCellElement: 'readonly',
|
|
28
|
+
Element: 'readonly',
|
|
29
|
+
Node: 'readonly',
|
|
30
|
+
NodeList: 'readonly',
|
|
31
|
+
Event: 'readonly',
|
|
32
|
+
CustomEvent: 'readonly',
|
|
33
|
+
// Test globals
|
|
34
|
+
describe: 'readonly',
|
|
35
|
+
it: 'readonly',
|
|
36
|
+
beforeEach: 'readonly',
|
|
37
|
+
afterEach: 'readonly',
|
|
38
|
+
expect: 'readonly',
|
|
39
|
+
sinon: 'readonly',
|
|
40
|
+
assert: 'readonly'
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
plugins: {
|
|
44
|
+
compat
|
|
45
|
+
},
|
|
46
|
+
rules: {
|
|
47
|
+
'compat/compat': 'error'
|
|
48
|
+
},
|
|
49
|
+
settings: {
|
|
50
|
+
polyfills: []
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
];
|
package/examples.html
CHANGED
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'nonce-a' 'nonce-b' 'nonce-c' 'nonce-d'" />
|
|
5
6
|
<title>DeCuE examples</title>
|
|
6
|
-
<style>
|
|
7
|
+
<style nonce="a">
|
|
8
|
+
object {
|
|
9
|
+
position:absolute;
|
|
10
|
+
left: -99999px;
|
|
11
|
+
}
|
|
7
12
|
main {
|
|
8
13
|
display: flex;
|
|
9
14
|
}
|
|
@@ -20,12 +25,12 @@
|
|
|
20
25
|
outline: 2px solid blue;
|
|
21
26
|
}
|
|
22
27
|
</style>
|
|
23
|
-
<script>
|
|
28
|
+
<script nonce="b">
|
|
24
29
|
function toLower(str) {
|
|
25
30
|
return str.toLowerCase();
|
|
26
31
|
}
|
|
27
32
|
</script>
|
|
28
|
-
<script>
|
|
33
|
+
<script nonce="c">
|
|
29
34
|
function validateme(ev) {
|
|
30
35
|
const target = ev.target;
|
|
31
36
|
if (target.value.indexOf('hello') < 0) {
|
|
@@ -53,13 +58,18 @@
|
|
|
53
58
|
parent.appendChild(el);
|
|
54
59
|
});
|
|
55
60
|
|
|
61
|
+
|
|
56
62
|
document.querySelectorAll('.move nested-element-with-attribute').forEach(el => {
|
|
57
|
-
el.parentNode.moveBefore
|
|
63
|
+
if (el.parentNode.moveBefore) {
|
|
64
|
+
el.parentNode.moveBefore(el, null);
|
|
65
|
+
} else {
|
|
66
|
+
el.parentNode.querySelector('.text').textContent = "(browser doesn't support moveBefore)";
|
|
67
|
+
}
|
|
58
68
|
});
|
|
59
69
|
}, 1000);
|
|
60
70
|
});
|
|
61
71
|
</script>
|
|
62
|
-
<script src="src/decue.js" debug elements="predefined-element predefined-element-with-attributes predefined-element-with-slot"></script>
|
|
72
|
+
<script src="src/decue.js" debug elements="predefined-element predefined-element-with-attributes predefined-element-with-slot" functions="toLower"></script>
|
|
63
73
|
</head>
|
|
64
74
|
<body>
|
|
65
75
|
|
|
@@ -79,7 +89,7 @@
|
|
|
79
89
|
</template>
|
|
80
90
|
<template decue="nesting-element">
|
|
81
91
|
<nested-element title="a nesting element"></nested-element>
|
|
82
|
-
<nested-element-with-attribute greetings="
|
|
92
|
+
<nested-element-with-attribute greetings="{greetings}" title="a nesting element with an attribute"></nested-element-with-attribute>
|
|
83
93
|
</template>
|
|
84
94
|
|
|
85
95
|
<template decue="default-slotted-element">
|
|
@@ -100,11 +110,11 @@
|
|
|
100
110
|
<template decue="element-with-function">
|
|
101
111
|
<div data-function="{greetings|toLower}" title="an element with a function {greetings|toLower}">Hello {greetings|toLower}</div>
|
|
102
112
|
</template>
|
|
103
|
-
<template decue="element-with-method" title="an element with a method {greetings
|
|
104
|
-
<div data-method="{greetings
|
|
113
|
+
<template decue="element-with-method" title="an element with a method {greetings.toUpperCase}">
|
|
114
|
+
<div data-method="{greetings.toUpperCase}">Hello {greetings.toUpperCase}</div>
|
|
105
115
|
</template>
|
|
106
116
|
<template decue="element-with-pipe">
|
|
107
|
-
<div data-piped="{greetings
|
|
117
|
+
<div data-piped="{greetings.toUpperCase|toLower}" title="an element with a piping function {greetings.toUpperCase|toLower}">Hello {greetings.toUpperCase|toLower}</div>
|
|
108
118
|
</template>
|
|
109
119
|
|
|
110
120
|
<template decue="predefined-element">
|
|
@@ -160,7 +170,7 @@
|
|
|
160
170
|
<td>
|
|
161
171
|
<fieldset>
|
|
162
172
|
<legend>Nesting</legend>
|
|
163
|
-
<nesting-element></nesting-element>
|
|
173
|
+
<nesting-element greetings="World!"></nesting-element>
|
|
164
174
|
</fieldset>
|
|
165
175
|
|
|
166
176
|
<fieldset>
|
|
@@ -202,7 +212,7 @@
|
|
|
202
212
|
<td>
|
|
203
213
|
<fieldset>
|
|
204
214
|
<legend>Nesting</legend>
|
|
205
|
-
<nesting-element shadow="open"></nesting-element>
|
|
215
|
+
<nesting-element greetings="World!" shadow="open"></nesting-element>
|
|
206
216
|
</fieldset>
|
|
207
217
|
|
|
208
218
|
<fieldset>
|
|
@@ -243,7 +253,7 @@
|
|
|
243
253
|
<td>
|
|
244
254
|
<fieldset>
|
|
245
255
|
<legend>Nesting</legend>
|
|
246
|
-
<nesting-element shadow="closed"></nesting-element>
|
|
256
|
+
<nesting-element greetings="World!" shadow="closed"></nesting-element>
|
|
247
257
|
</fieldset>
|
|
248
258
|
|
|
249
259
|
<fieldset>
|
|
@@ -288,18 +298,21 @@
|
|
|
288
298
|
<fieldset class="remove-then-append">
|
|
289
299
|
<legend>remove-then-append</legend>
|
|
290
300
|
<nested-element-with-attribute greetings="World!"></nested-element-with-attribute>
|
|
301
|
+
<div class="text">text originally in the end</div>
|
|
291
302
|
</fieldset>
|
|
292
303
|
</td>
|
|
293
304
|
<td>
|
|
294
305
|
<fieldset class="remove-then-append">
|
|
295
306
|
<legend>remove-then-append</legend>
|
|
296
307
|
<nested-element-with-attribute greetings="World!" shadow="open"></nested-element-with-attribute>
|
|
308
|
+
<div class="text">text originally in the end</div>
|
|
297
309
|
</fieldset>
|
|
298
310
|
</td>
|
|
299
311
|
<td>
|
|
300
312
|
<fieldset class="remove-then-append">
|
|
301
313
|
<legend>remove-then-append</legend>
|
|
302
314
|
<nested-element-with-attribute greetings="World!" shadow="closed"></nested-element-with-attribute>
|
|
315
|
+
<div class="text">text originally in the end</div>
|
|
303
316
|
</fieldset>
|
|
304
317
|
</td>
|
|
305
318
|
</tr>
|
|
@@ -309,24 +322,27 @@
|
|
|
309
322
|
<fieldset class="move">
|
|
310
323
|
<legend>move</legend>
|
|
311
324
|
<nested-element-with-attribute greetings="World!"></nested-element-with-attribute>
|
|
325
|
+
<div class="text">text originally in the end</div>
|
|
312
326
|
</fieldset>
|
|
313
327
|
</td>
|
|
314
328
|
<td>
|
|
315
329
|
<fieldset class="move">
|
|
316
330
|
<legend>move</legend>
|
|
317
331
|
<nested-element-with-attribute greetings="World!" shadow="open"></nested-element-with-attribute>
|
|
332
|
+
<div class="text">text originally in the end</div>
|
|
318
333
|
</fieldset>
|
|
319
334
|
</td>
|
|
320
335
|
<td>
|
|
321
336
|
<fieldset class="move">
|
|
322
337
|
<legend>move</legend>
|
|
323
338
|
<nested-element-with-attribute greetings="World!" shadow="closed"></nested-element-with-attribute>
|
|
339
|
+
<div class="text">text originally in the end</div>
|
|
324
340
|
</fieldset>
|
|
325
341
|
</td>
|
|
326
342
|
</tr>
|
|
327
343
|
</table>
|
|
328
344
|
</main>
|
|
329
345
|
|
|
330
|
-
<object data="external.html" type="text/html"
|
|
346
|
+
<object nonce="d" data="external.html" type="text/html"></object>
|
|
331
347
|
</body>
|
|
332
348
|
</html>
|
package/npm.sh
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decue",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Declarative Custom Elements. Create simple web components with just HTML, parameterized with slots and attributes. With or without shadow-dom.",
|
|
5
5
|
"author": "Jyri-Matti Lähteenmäki <jyri-matti@lahteenmaki.net>",
|
|
6
6
|
"keywords": [
|
|
@@ -9,9 +9,16 @@
|
|
|
9
9
|
"custom-element"
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
+
"browserslist": [
|
|
13
|
+
"since 2010"
|
|
14
|
+
],
|
|
12
15
|
"scripts": {
|
|
13
|
-
"test": "
|
|
14
|
-
"
|
|
16
|
+
"test": "npx playwright install && web-test-runner",
|
|
17
|
+
"test:watch": "web-test-runner --watch",
|
|
18
|
+
"lint": "eslint src/",
|
|
19
|
+
"lint:fix": "eslint src/ --fix",
|
|
20
|
+
"compat": "eslint src/ --rule 'compat/compat: error'",
|
|
21
|
+
"dist": "npm test && npm run lint && npm run compat && cp -r src/* dist/ && npm run-script uglify",
|
|
15
22
|
"uglify": "uglifyjs -m eval -o dist/decue.min.js dist/decue.js"
|
|
16
23
|
},
|
|
17
24
|
"repository": {
|
|
@@ -19,11 +26,13 @@
|
|
|
19
26
|
"url": "https://codeberg.org/jyri-matti/decue.git"
|
|
20
27
|
},
|
|
21
28
|
"devDependencies": {
|
|
22
|
-
"chai": "^4.3.
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
29
|
+
"@esm-bundle/chai": "^4.3.4",
|
|
30
|
+
"@web/test-runner": "^0.18.0",
|
|
31
|
+
"@web/test-runner-playwright": "^0.11.0",
|
|
32
|
+
"browserslist": "^4.28.1",
|
|
33
|
+
"eslint": "^9.39.2",
|
|
34
|
+
"eslint-plugin-compat": "^6.1.0",
|
|
35
|
+
"sinon": "^17.0.0",
|
|
27
36
|
"uglify-js": "^3.15.0"
|
|
28
37
|
}
|
|
29
38
|
}
|