Comparing version 8.1.5 to 8.1.6
@@ -5,2 +5,16 @@ # Changelog | ||
### [8.1.6](https://github.com/hybridsjs/hybrids/compare/v8.1.5...v8.1.6) (2022-10-31) | ||
### Bug Fixes | ||
* **cache:** remove suspend feature ([30ac94e](https://github.com/hybridsjs/hybrids/commit/30ac94e91b9c2c9fb6276320d83e0d344c77ae0b)) | ||
* **define:** defer connect & disconnect callbacks ([cd964a5](https://github.com/hybridsjs/hybrids/commit/cd964a5374e757c7f00af900b9ff33f87b5e81ef)) | ||
* refactor forEach to for-of in core sources ([c2e71c0](https://github.com/hybridsjs/hybrids/commit/c2e71c0708b27de2966f111224ae520175167f97)) | ||
* refactor reduce to for-of in core sources ([7918737](https://github.com/hybridsjs/hybrids/commit/7918737a73365ec7b5551e57b9ccc7db016caa2c)) | ||
* **router:** clean up dev code ([e16e35f](https://github.com/hybridsjs/hybrids/commit/e16e35fce8caf0e87a54c74a8c7dc15e094897df)) | ||
* **router:** clear history state when root router disconnects ([2f73e81](https://github.com/hybridsjs/hybrids/commit/2f73e81f8c43411b16f2e5cd5fa0326cc61e0856)) | ||
* **store:** listing model stringifies to its id ([82da9a1](https://github.com/hybridsjs/hybrids/commit/82da9a113b5238762a1cd5255d939c053c08979a)) | ||
* **store:** prototype should not contain enumerable properties ([8b16963](https://github.com/hybridsjs/hybrids/commit/8b1696328fd39371bd88983ce2fbe817f18d618b)) | ||
### [8.1.5](https://github.com/hybridsjs/hybrids/compare/v8.1.4...v8.1.5) (2022-09-27) | ||
@@ -7,0 +21,0 @@ |
@@ -304,3 +304,3 @@ # Structure | ||
When you insert, remove, or relocate an element in the DOM tree, `connect` and `disconnect` is called synchronously (in the `connectedCallback` and `disconnectedCallback` callbacks of the Custom Elements API). | ||
When you insert, remove, or relocate an element in the DOM tree, `connect` and `disconnect` methods are called (by the `connectedCallback` and `disconnectedCallback` callbacks of the Custom Elements API). | ||
@@ -335,8 +335,2 @@ ```javascript | ||
The `invalidate` callback can take options object argument with the following properties: | ||
| Option | Type | Default | Description | | ||
| ------ | --------- | ------- | --------------------------------------------------------------------------------------------------------------- | | ||
| force | `boolean` | false | When true, the invalidate call will always trigger an update, even if the property's identity has not changed | | ||
### `observe` | ||
@@ -343,0 +337,0 @@ |
@@ -38,3 +38,1 @@ # Getting Started | ||
The library source code uses ES modules and currently supported JavaScript syntax by all of the major browsers. You can use hybrids in all modern browsers without code transpilation and bundling. | ||
[![Build Status](https://app.saucelabs.com/browser-matrix/hybrids.svg)](https://app.saucelabs.com/open_sauce/user/hybrids/builds) |
<center> | ||
<h1> | ||
<img alt="hybrids" src="https://raw.githubusercontent.com/hybridsjs/hybrids/master/docs/assets/hybrids-full-logo.svg?sanitize=true" align="center"> | ||
<img alt="hybrids" src="https://raw.githubusercontent.com/hybridsjs/hybrids/main/docs/assets/hybrids-full-logo.svg?sanitize=true" align="center"> | ||
</h1> | ||
@@ -8,4 +8,4 @@ </center> | ||
[![npm version](https://img.shields.io/npm/v/hybrids.svg?style=flat)](https://www.npmjs.com/package/hybrids) | ||
[![build status](https://img.shields.io/travis/hybridsjs/hybrids/master.svg?style=flat)](https://app.travis-ci.com/github/hybridsjs/hybrids) | ||
[![coverage status](https://img.shields.io/coveralls/github/hybridsjs/hybrids.svg?style=flat)](https://coveralls.io/github/hybridsjs/hybrids?branch=master) | ||
[![build status](https://img.shields.io/travis/hybridsjs/hybrids/main.svg?style=flat)](https://app.travis-ci.com/github/hybridsjs/hybrids) | ||
[![coverage status](https://img.shields.io/coveralls/github/hybridsjs/hybrids.svg?style=flat)](https://coveralls.io/github/hybridsjs/hybrids?branch=main) | ||
@@ -12,0 +12,0 @@ **hybrids** is a JavaScript UI framework for creating fully-featured web applications, components libraries, or single web components with unique mixed declarative and functional architecture. |
@@ -45,3 +45,3 @@ # Usage | ||
### `url` | ||
### `options.url` | ||
@@ -58,3 +58,3 @@ If your application uses a mixed approach - views with and without URLs, you should specify a base URL to avoid not deterministic behavior of the router for views without the URL. Otherwise, the router will use an entry point as a base URL (which can be different according to the use case). | ||
### `params` | ||
### `options.params` | ||
@@ -75,3 +75,3 @@ Regardless of the explicit parameters when navigating to the view, you can specify an array of properties of the component, which are passed to every view as a parameter. They bypass the URL generation, so they are set by the reference, and they are not included in the URL. | ||
user: store(User), | ||
views: router(Home, { params: ["settings"] }), | ||
views: router(Home, { params: ["user"] }), | ||
... | ||
@@ -78,0 +78,0 @@ }); |
@@ -23,3 +23,3 @@ # Usage | ||
### get | ||
### `store.get` | ||
@@ -36,3 +36,3 @@ ```typescript | ||
The `store.get` method always returns an object - model instance or a model placeholder. If the model source is synchronous (memory-based or external sync source, like `localStorage`), the get method immediately returns an instance. Otherwise, before the cache has an instance of the model, the placeholder is returned instead. When the promise resolves, the next call to the store returns an instance. The cache takes care to notify the component that data has changed (if you need to use this method outside of the component definition, you can use the `store.pending()` guard to access the returned promise). | ||
The `store.get()` method always returns an object - model instance or a model placeholder. If the model source is synchronous (memory-based or external sync source, like `localStorage`), the get method immediately returns an instance. Otherwise, before the cache has an instance of the model, the placeholder is returned instead. When the promise resolves, the next call to the store returns an instance. The cache takes care to notify the component that data has changed (if you need to use this method outside of the component definition, you can use the `store.pending()` guard to access the returned promise). | ||
@@ -59,3 +59,3 @@ ```javascript | ||
### set | ||
### `store.set` | ||
@@ -120,3 +120,3 @@ The `store.set()` method can create a new instance or update an existing model. According to the mode, the first argument should be a model definition or a model instance. | ||
The `store.set` supports partial values for updating the model only with a subset of values. If your model has nested object structures, you can update them partially as well: | ||
The `store.set()` supports partial values for updating the model only with a subset of values. If your model has nested object structures, you can update them partially as well: | ||
@@ -129,3 +129,3 @@ ```javascript | ||
### resolve | ||
### `store.resolve` | ||
@@ -180,3 +180,3 @@ You can use the `store.resolve()` method to simplify access to pending model instances, which can be updated at the moment. The function returns a promise resolving into the current model instance, regardless of the pending state. It also supports multiple chains of set methods, so the result will always be the latest instance. | ||
### sync | ||
### `store.sync` | ||
@@ -223,3 +223,3 @@ The storage methods are called only for the user interaction - when the model is got, or when a new value for the model instance is set. However, there might be a case, where your model instance is been updated outside of the user scope, for example by the server. | ||
### clear | ||
### `store.clear` | ||
@@ -466,3 +466,3 @@ Both memory and external storage uses a global cache mechanism based on the model definition reference. Model instances are global, so the cache mechanism cannot automatically predict which instance is no longer required. Because of that, the store provides the `store.clear()` method for invalidating model instances by the model definition or specific instance of the model. | ||
### ready | ||
### `store.ready` | ||
@@ -503,3 +503,3 @@ ```typescript | ||
### pending | ||
### `store.pending` | ||
@@ -519,3 +519,3 @@ ```typescript | ||
### error | ||
### `store.error` | ||
@@ -522,0 +522,0 @@ ```typescript |
{ | ||
"name": "hybrids", | ||
"version": "8.1.5", | ||
"version": "8.1.6", | ||
"description": "A JavaScript framework for creating fully-featured web applications, components libraries, and single web components with unique declarative and functional architecture", | ||
@@ -47,3 +47,2 @@ "type": "module", | ||
"karma-jasmine": "^5.1.0", | ||
"karma-sauce-launcher": "^4.3.5", | ||
"prettier": "^2.5.1", | ||
@@ -50,0 +49,0 @@ "standard-version": "^9.3.2" |
<center> | ||
<h1> | ||
<img alt="" src="https://raw.githubusercontent.com/hybridsjs/hybrids/master/docs/assets/hybrids-full-logo.svg?sanitize=true" align="center"> | ||
<img alt="" src="https://raw.githubusercontent.com/hybridsjs/hybrids/main/docs/assets/hybrids-full-logo.svg?sanitize=true" align="center"> | ||
</h1> | ||
@@ -9,4 +9,4 @@ </center> | ||
[![npm version](https://img.shields.io/npm/v/hybrids.svg?style=flat)](https://www.npmjs.com/package/hybrids) | ||
[![build status](https://img.shields.io/travis/hybridsjs/hybrids/master.svg?style=flat)](https://app.travis-ci.com/github/hybridsjs/hybrids) | ||
[![coverage status](https://img.shields.io/coveralls/github/hybridsjs/hybrids.svg?style=flat)](https://coveralls.io/github/hybridsjs/hybrids?branch=master) | ||
[![build status](https://img.shields.io/travis/hybridsjs/hybrids/main.svg?style=flat)](https://app.travis-ci.com/github/hybridsjs/hybrids) | ||
[![coverage status](https://img.shields.io/coveralls/github/hybridsjs/hybrids.svg?style=flat)](https://coveralls.io/github/hybridsjs/hybrids?branch=main) | ||
@@ -13,0 +13,0 @@ **hybrids** is a JavaScript UI framework for creating fully-featured web applications, components libraries, or single web components with unique mixed declarative and functional architecture. |
255
src/cache.js
@@ -1,29 +0,55 @@ | ||
import global from "./global.js"; | ||
import * as emitter from "./emitter.js"; | ||
const entries = new WeakMap(); | ||
const suspense = new WeakSet(); | ||
const stack = new Set(); | ||
function dispatch(entry) { | ||
const contexts = new Set(); | ||
const iterator = contexts.values(); | ||
while (entry) { | ||
entry.resolved = false; | ||
if (entry.deps) { | ||
for (const depEntry of entry.deps) { | ||
depEntry.contexts.delete(entry); | ||
} | ||
entry.deps.clear(); | ||
} | ||
if (entry.contexts) { | ||
for (const context of entry.contexts) { | ||
contexts.add(context); | ||
} | ||
entry.contexts.clear(); | ||
} | ||
if (entry.observe) { | ||
emitter.add(entry.observe); | ||
} | ||
entry = iterator.next().value; | ||
} | ||
} | ||
export function getEntry(target, key) { | ||
let targetMap = entries.get(target); | ||
if (!targetMap) { | ||
targetMap = new Map(); | ||
entries.set(target, targetMap); | ||
let map = entries.get(target); | ||
if (!map) { | ||
map = new Map(); | ||
entries.set(target, map); | ||
} | ||
let entry = targetMap.get(key); | ||
let entry = map.get(key); | ||
if (!entry) { | ||
entry = { | ||
key, | ||
target, | ||
key, | ||
value: undefined, | ||
lastValue: undefined, | ||
contexts: new Set(), | ||
deps: new Set(), | ||
state: 0, | ||
depState: 0, | ||
resolved: false, | ||
contexts: undefined, | ||
deps: undefined, | ||
observe: undefined, | ||
}; | ||
targetMap.set(key, entry); | ||
map.set(key, entry); | ||
} | ||
@@ -35,117 +61,42 @@ | ||
export function getEntries(target) { | ||
const result = []; | ||
const targetMap = entries.get(target); | ||
if (targetMap) { | ||
targetMap.forEach((entry) => { | ||
result.push(entry); | ||
}); | ||
} | ||
return result; | ||
if (targetMap) return [...targetMap.values()]; | ||
return []; | ||
} | ||
function cleanContexts(entry) { | ||
entry.contexts.forEach((contextEntry) => { | ||
if (suspense.has(contextEntry.target)) { | ||
Object.assign(contextEntry, { | ||
depState: 0, | ||
resolved: false, | ||
}); | ||
entry.contexts.delete(contextEntry); | ||
cleanContexts(contextEntry); | ||
} | ||
}); | ||
} | ||
function dispatchDeep(entry) { | ||
entry.resolved = false; | ||
if (!suspense.has(entry.target)) { | ||
emitter.dispatch(entry); | ||
} | ||
cleanContexts(entry); | ||
entry.contexts.forEach(dispatchDeep); | ||
} | ||
let context = null; | ||
const contexts = new Set(); | ||
export function get(target, key, getter) { | ||
const entry = getEntry(target, key); | ||
if (context && !suspense.has(context.target)) { | ||
if (context) { | ||
if (!entry.contexts) entry.contexts = new Set(); | ||
if (!context.deps) context.deps = new Set(); | ||
entry.contexts.add(context); | ||
context.deps.add(entry); | ||
entry.contexts.add(context); | ||
} | ||
if (!suspense.has(target)) { | ||
cleanContexts(entry); | ||
if (entry.resolved) return entry.value; | ||
if (entry.resolved) { | ||
return entry.value; | ||
} | ||
if (entry.depState > entry.state) { | ||
let depState = entry.state; | ||
for (const depEntry of entry.deps) { | ||
// eslint-disable-next-line no-unused-expressions | ||
depEntry.target[depEntry.key]; | ||
if (!depEntry.resolved) { | ||
depState = false; | ||
break; | ||
} | ||
depState += depEntry.state; | ||
} | ||
if (depState && depState === entry.depState) { | ||
entry.resolved = true; | ||
return entry.value; | ||
} | ||
} | ||
} | ||
const lastContext = context; | ||
try { | ||
if (contexts.has(entry)) { | ||
if (stack.has(entry)) { | ||
throw Error(`Circular get invocation is forbidden: '${key}'`); | ||
} | ||
entry.deps.forEach((depEntry) => { | ||
depEntry.contexts.delete(entry); | ||
}); | ||
entry.deps.clear(); | ||
context = entry; | ||
contexts.add(entry); | ||
stack.add(entry); | ||
const nextValue = getter(target, entry.value); | ||
entry.value = getter(target, entry.value); | ||
entry.resolved = true; | ||
context = lastContext; | ||
if (nextValue !== entry.value) { | ||
entry.value = nextValue; | ||
entry.state += 1; | ||
} | ||
let depState = entry.state; | ||
entry.deps.forEach((depEntry) => { | ||
depState += depEntry.state; | ||
}); | ||
entry.depState = depState; | ||
entry.resolved = !suspense.has(target); | ||
contexts.delete(entry); | ||
stack.delete(entry); | ||
} catch (e) { | ||
context = lastContext; | ||
contexts.delete(entry); | ||
stack.delete(entry); | ||
entry.resolved = false; | ||
if (context && !suspense.has(context)) { | ||
if (context) { | ||
context.deps.delete(entry); | ||
@@ -167,18 +118,39 @@ entry.contexts.delete(context); | ||
entry.value = newValue; | ||
entry.state += 1; | ||
entry.depState = 0; | ||
dispatchDeep(entry); | ||
dispatch(entry); | ||
} | ||
} | ||
const gcList = new Set(); | ||
export function observe(target, key, getter, fn) { | ||
const entry = getEntry(target, key); | ||
entry.observe = () => { | ||
const value = get(target, key, getter); | ||
if (value !== entry.lastValue) { | ||
fn(target, value, entry.lastValue); | ||
entry.lastValue = value; | ||
} | ||
}; | ||
emitter.add(entry.observe); | ||
return () => { | ||
emitter.clear(entry.observe); | ||
entry.observe = undefined; | ||
entry.lastValue = undefined; | ||
}; | ||
} | ||
const gc = new Set(); | ||
function deleteEntry(entry) { | ||
if (!gcList.size) { | ||
global.requestAnimationFrame(() => { | ||
gcList.forEach((e) => { | ||
if (e.contexts.size === 0) { | ||
e.deps.forEach((depEntry) => { | ||
depEntry.contexts.delete(e); | ||
}); | ||
if (!gc.size) { | ||
setTimeout(() => { | ||
for (const e of gc) { | ||
if (!e.contexts || e.contexts.size === 0) { | ||
if (e.deps) { | ||
for (const depEntry of e.deps) { | ||
depEntry.contexts.delete(e); | ||
} | ||
} | ||
@@ -188,13 +160,13 @@ const targetMap = entries.get(e.target); | ||
} | ||
}); | ||
gcList.clear(); | ||
} | ||
gc.clear(); | ||
}); | ||
} | ||
gcList.add(entry); | ||
gc.add(entry); | ||
} | ||
function invalidateEntry(entry, options) { | ||
entry.depState = 0; | ||
dispatchDeep(entry); | ||
dispatch(entry); | ||
@@ -209,15 +181,5 @@ if (options.clearValue) { | ||
} | ||
if (options.force) { | ||
entry.state += 1; | ||
} | ||
} | ||
export function invalidate(target, key, options = {}) { | ||
if (contexts.size) { | ||
throw Error( | ||
`Invalidating property in chain of get calls is forbidden: '${key}'`, | ||
); | ||
} | ||
const entry = getEntry(target, key); | ||
@@ -228,35 +190,8 @@ invalidateEntry(entry, options); | ||
export function invalidateAll(target, options = {}) { | ||
if (contexts.size) { | ||
throw Error( | ||
"Invalidating all properties in chain of get calls is forbidden", | ||
); | ||
} | ||
const targetMap = entries.get(target); | ||
if (targetMap) { | ||
targetMap.forEach((entry) => { | ||
for (const entry of targetMap.values()) { | ||
invalidateEntry(entry, options); | ||
}); | ||
} | ||
} | ||
} | ||
export function observe(target, key, getter, fn) { | ||
const entry = getEntry(target, key); | ||
return emitter.subscribe(entry, () => { | ||
const value = get(target, key, getter); | ||
if (value !== entry.lastValue) { | ||
fn(target, value, entry.lastValue); | ||
entry.lastValue = value; | ||
} | ||
}); | ||
} | ||
export function suspend(target) { | ||
suspense.add(target); | ||
} | ||
export function unsuspend(target) { | ||
suspense.delete(target); | ||
} |
import global from "./global.js"; | ||
function walk(node, fn, options, items = [], host = node) { | ||
Array.from(node.children).forEach((child) => { | ||
for (const child of Array.from(node.children)) { | ||
const hybrids = child.constructor.hybrids; | ||
@@ -14,3 +14,3 @@ if (hybrids && fn(hybrids, host)) { | ||
} | ||
}); | ||
} | ||
@@ -17,0 +17,0 @@ return items; |
import global from "./global.js"; | ||
import * as cache from "./cache.js"; | ||
import * as emitter from "./emitter.js"; | ||
import { deferred, camelToDash, walkInShadow } from "./utils.js"; | ||
const propsMap = new WeakMap(); | ||
import render from "./render.js"; | ||
import value from "./value.js"; | ||
const disconnects = new WeakMap(); | ||
function compile(hybrids, HybridsElement) { | ||
if (HybridsElement) { | ||
if (hybrids === HybridsElement.hybrids) return HybridsElement; | ||
export const callbacksMap = new WeakMap(); | ||
for (const key of Object.keys(HybridsElement.hybrids)) { | ||
delete HybridsElement.prototype[key]; | ||
} | ||
} else { | ||
HybridsElement = class extends global.HTMLElement { | ||
connectedCallback() { | ||
for (const key of Object.keys(this)) { | ||
const value = this[key]; | ||
delete this[key]; | ||
this[key] = value; | ||
} | ||
class HybridsRootElement extends global.HTMLElement { | ||
constructor() { | ||
super(); | ||
const set = new Set(); | ||
disconnects.set(this, set); | ||
const props = propsMap.get(this.constructor); | ||
for (let index = 0; index < props.length; index += 1) { | ||
const key = props[index]; | ||
if (hasOwnProperty.call(this, key)) { | ||
const value = this[key]; | ||
delete this[key]; | ||
this[key] = value; | ||
emitter.add(() => { | ||
if (set === disconnects.get(this)) { | ||
for (const fn of this.constructor.connects) set.add(fn(this)); | ||
} | ||
}); | ||
} | ||
} | ||
cache.suspend(this); | ||
} | ||
disconnectedCallback() { | ||
const callbacks = disconnects.get(this); | ||
connectedCallback() { | ||
cache.unsuspend(this); | ||
const callbacks = callbacksMap.get(this.constructor); | ||
const list = []; | ||
for (let index = 0; index < callbacks.length; index += 1) { | ||
const cb = callbacks[index](this); | ||
if (cb) list.push(cb); | ||
} | ||
disconnects.set(this, list); | ||
} | ||
disconnectedCallback() { | ||
cache.suspend(this); | ||
const list = disconnects.get(this); | ||
for (let index = 0; index < list.length; index += 1) { | ||
list[index](); | ||
} | ||
} | ||
} | ||
function render(fn, useShadow) { | ||
return { | ||
get: useShadow | ||
? (host) => { | ||
const updateDOM = fn(host); | ||
const target = | ||
host.shadowRoot || | ||
host.attachShadow({ | ||
mode: "open", | ||
delegatesFocus: fn.delegatesFocus || false, | ||
}); | ||
return () => { | ||
updateDOM(host, target); | ||
return target; | ||
}; | ||
for (const fn of callbacks) { | ||
if (fn) fn(); | ||
} | ||
: (host) => { | ||
const updateDOM = fn(host); | ||
return () => { | ||
updateDOM(host, host); | ||
return host; | ||
}; | ||
}, | ||
observe(host, flush) { flush(); }, // prettier-ignore | ||
}; | ||
} | ||
const transforms = { | ||
string: String, | ||
number: Number, | ||
boolean: Boolean, | ||
undefined: (v) => v, | ||
}; | ||
function property(key, desc) { | ||
const type = typeof desc.value; | ||
const transform = transforms[type]; | ||
if (!transform) { | ||
throw TypeError( | ||
`Invalid default value for '${key}' property - it must be a string, number, boolean or undefined: ${type}`, | ||
); | ||
} | ||
const defaultValue = desc.value; | ||
const attrName = camelToDash(key); | ||
const setAttr = (host, value) => { | ||
if ( | ||
(!value && value !== 0) || | ||
(typeof value === "object" && value.toString() === undefined) | ||
) { | ||
host.removeAttribute(attrName); | ||
} else { | ||
host.setAttribute(attrName, type === "boolean" ? "" : value); | ||
} | ||
return value; | ||
}; | ||
return { | ||
get: (host, value) => { | ||
if (value === undefined) { | ||
if (host.hasAttribute(attrName)) { | ||
value = transform(type === "boolean" || host.getAttribute(attrName)); | ||
} else { | ||
return defaultValue; | ||
} | ||
disconnects.delete(this); | ||
cache.invalidateAll(this); | ||
} | ||
return value; | ||
}, | ||
set: | ||
type !== "undefined" | ||
? (host, value) => setAttr(host, transform(value)) | ||
: (host, value) => value, | ||
connect: | ||
type !== "undefined" | ||
? (host, _, invalidate) => { | ||
if (!host.hasAttribute(attrName) && host[key] === defaultValue) { | ||
setAttr(host, defaultValue); | ||
} | ||
return desc.connect && desc.connect(host, _, invalidate); | ||
} | ||
: desc.connect, | ||
observe: desc.observe, | ||
}; | ||
} | ||
function compile(hybrids, HybridsElement) { | ||
if (HybridsElement) { | ||
if (hybrids === HybridsElement.hybrids) return HybridsElement; | ||
propsMap.get(HybridsElement).forEach((key) => { | ||
delete HybridsElement.prototype[key]; | ||
}); | ||
} else { | ||
HybridsElement = class extends HybridsRootElement {}; | ||
}; | ||
} | ||
@@ -152,11 +51,7 @@ | ||
const callbacks = []; | ||
const props = Object.keys(hybrids); | ||
const connects = new Set(); | ||
callbacksMap.set(HybridsElement, callbacks); | ||
propsMap.set(HybridsElement, props); | ||
for (const key of Object.keys(hybrids)) { | ||
if (key === "tag") continue; | ||
props.forEach((key) => { | ||
if (key === "tag") return; | ||
let desc = hybrids[key]; | ||
@@ -176,2 +71,8 @@ const type = typeof desc; | ||
} else if (desc.set) { | ||
if (hasOwnProperty.call(desc, "value")) { | ||
throw TypeError( | ||
`Invalid property descriptor for '${key}' property - it must not have 'value' and 'set' properties at the same time.`, | ||
); | ||
} | ||
const attrName = camelToDash(key); | ||
@@ -188,3 +89,3 @@ const get = desc.get || ((host, value) => value); | ||
if (hasOwnProperty.call(desc, "value")) { | ||
desc = property(key, desc); | ||
desc = value(key, desc); | ||
} else if (!desc.get) { | ||
@@ -209,20 +110,17 @@ throw TypeError( | ||
if (desc.observe) { | ||
callbacks.unshift((host) => | ||
cache.observe(host, key, desc.get, desc.observe), | ||
if (desc.connect) { | ||
connects.add((host) => | ||
desc.connect(host, key, () => { | ||
cache.invalidate(host, key); | ||
}), | ||
); | ||
} | ||
if (desc.connect) { | ||
callbacks.push((host) => { | ||
function invalidate(options) { | ||
cache.invalidate(host, key, { | ||
force: typeof options === "object" && options.force === true, | ||
}); | ||
} | ||
return desc.connect(host, key, invalidate); | ||
}); | ||
if (desc.observe) { | ||
connects.add((host) => cache.observe(host, key, desc.get, desc.observe)); | ||
} | ||
}); | ||
} | ||
HybridsElement.connects = connects; | ||
return HybridsElement; | ||
@@ -241,3 +139,3 @@ } | ||
Object.keys(hybrids).forEach((key) => { | ||
for (const key of Object.keys(hybrids)) { | ||
const type = typeof hybrids[key]; | ||
@@ -249,3 +147,3 @@ const clearValue = | ||
cache.invalidate(node, key, { clearValue }); | ||
}); | ||
} | ||
@@ -252,0 +150,0 @@ node.connectedCallback(); |
@@ -1,40 +0,23 @@ | ||
import global from "./global.js"; | ||
import { deferred } from "./utils.js"; | ||
const callbacks = new WeakMap(); | ||
const queue = new Set(); | ||
export function add(fn) { | ||
if (!queue.size) deferred.then(execute); | ||
queue.add(fn); | ||
} | ||
function execute() { | ||
try { | ||
queue.forEach((target) => { | ||
try { | ||
callbacks.get(target)(); | ||
queue.delete(target); | ||
} catch (e) { | ||
queue.delete(target); | ||
throw e; | ||
} | ||
}); | ||
} catch (e) { | ||
if (queue.size) execute(); | ||
throw e; | ||
} | ||
export function clear(fn) { | ||
queue.delete(fn); | ||
} | ||
export function dispatch(target) { | ||
if (callbacks.has(target)) { | ||
if (!queue.size) { | ||
global.requestAnimationFrame(execute); | ||
function execute() { | ||
for (const fn of queue) { | ||
try { | ||
fn(); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
queue.add(target); | ||
} | ||
} | ||
export function subscribe(target, cb) { | ||
callbacks.set(target, cb); | ||
dispatch(target); | ||
return function unsubscribe() { | ||
queue.delete(target); | ||
callbacks.delete(target); | ||
}; | ||
queue.clear(); | ||
} |
@@ -1,14 +0,4 @@ | ||
/* eslint-disable no-undef */ | ||
export function polyfill(global) { | ||
global = Object.create(global); | ||
if (!("requestAnimationFrame" in global)) { | ||
Object.defineProperty(global, "requestAnimationFrame", { | ||
value: function requestAnimationFrame(callback) { | ||
return setTimeout(callback, 0); | ||
}, | ||
}); | ||
} | ||
if (!("HTMLElement" in global)) { | ||
@@ -39,3 +29,3 @@ Object.defineProperty(global, "HTMLElement", { | ||
/* istanbul ignore next */ | ||
/* istanbul ignore next */ // eslint-disable-next-line no-undef | ||
export default typeof window === "object" ? window : polyfill(globalThis); |
@@ -153,5 +153,8 @@ import { getPlaceholder } from "./template/utils.js"; | ||
function getString(parts, args) { | ||
const string = parts.reduce( | ||
(acc, part, index) => `${acc}\${${index - 1}}${part}`, | ||
); | ||
let string = ""; | ||
for (const [index, part] of parts.entries()) { | ||
string += index ? `\${${index - 1}}${part}` : part; | ||
} | ||
const [key, , context = ""] = string.split("|"); | ||
@@ -158,0 +161,0 @@ |
@@ -27,9 +27,5 @@ function walk(node, fn) { | ||
connect(host, key, invalidate) { | ||
const target = host[key]; | ||
if (target) { | ||
return invalidate; | ||
} | ||
return false; | ||
return invalidate; | ||
}, | ||
}; | ||
} |
import global from "./global.js"; | ||
import { callbacksMap } from "./define.js"; | ||
import * as cache from "./cache.js"; | ||
import { dispatch, walkInShadow } from "./utils.js"; | ||
import { deferred, dispatch, walkInShadow } from "./utils.js"; | ||
@@ -73,6 +72,6 @@ const connect = Symbol("router.connect"); | ||
map.forEach((pos, el) => { | ||
el.scrollLeft = clear ? 0 : pos.left; | ||
el.scrollTop = clear ? 0 : pos.top; | ||
}); | ||
for (const [el, { left, top }] of map) { | ||
el.scrollLeft = clear ? 0 : left; | ||
el.scrollTop = clear ? 0 : top; | ||
} | ||
@@ -117,3 +116,5 @@ scrollMap.delete(target); | ||
url(params, strict = false) { | ||
const temp = normalizedPathname.reduce((acc, part) => { | ||
let temp = ""; | ||
for (let part of normalizedPathname) { | ||
if (part.startsWith(":")) { | ||
@@ -129,8 +130,8 @@ const key = part.slice(1); | ||
return `${acc}/${part}`; | ||
}, ""); | ||
temp += `/${part}`; | ||
} | ||
const url = new URL(temp, global.location.origin); | ||
Object.keys(params).forEach((key) => { | ||
for (const key of Object.keys(params)) { | ||
if ( | ||
@@ -140,7 +141,7 @@ pathnameParams.includes(key) || | ||
) { | ||
return; | ||
continue; | ||
} | ||
url.searchParams.append(key, mapUrlParam(params[key])); | ||
}); | ||
} | ||
@@ -167,5 +168,5 @@ return url; | ||
url.searchParams.forEach((value, key) => { | ||
for (const [key, value] of url.searchParams) { | ||
params[key] = value; | ||
}); | ||
} | ||
@@ -188,3 +189,6 @@ return params; | ||
} | ||
config.stack.forEach(addEntryPoint); | ||
for (const child of config.stack) { | ||
addEntryPoint(child); | ||
} | ||
} | ||
@@ -205,3 +209,2 @@ | ||
// eslint-disable-next-line no-use-before-define | ||
return setupView(hybrids, options, parent, nestedParent); | ||
@@ -270,10 +273,10 @@ }); | ||
const callbacks = callbacksMap.get(Constructor); | ||
const { connects } = Constructor; | ||
if (!nestedParent && !options.dialog) { | ||
callbacks.push(restoreLayout); | ||
connects.add(restoreLayout); | ||
} | ||
if (options.dialog) { | ||
callbacks.push((host) => { | ||
connects.add((host) => { | ||
const goBackOnEscKey = (event) => { | ||
@@ -295,4 +298,2 @@ if (event.key === "Escape") { | ||
console.log({ prevActiveEl }); | ||
root.addEventListener("focusin", focusDialog); | ||
@@ -315,6 +316,7 @@ root.addEventListener("focusout", focusDialog); | ||
const writableParams = []; | ||
Object.keys(Constructor.prototype).forEach((key) => { | ||
for (const key of Object.keys(Constructor.prototype)) { | ||
const desc = Object.getOwnPropertyDescriptor(Constructor.prototype, key); | ||
if (desc.set) writableParams.push(key); | ||
}); | ||
} | ||
@@ -334,3 +336,3 @@ if (options.url) { | ||
browserUrl.paramsKeys.forEach((key) => { | ||
for (const key of browserUrl.paramsKeys) { | ||
const desc = Object.getOwnPropertyDescriptor( | ||
@@ -347,3 +349,3 @@ Constructor.prototype, | ||
} | ||
}); | ||
} | ||
} | ||
@@ -358,15 +360,17 @@ | ||
callbacksMap.get(Constructor).unshift((_) => | ||
connects.add((_) => | ||
cache.observe( | ||
_, | ||
connect, | ||
(host) => | ||
stateParams.reduce((acc, key) => { | ||
(host) => { | ||
const params = {}; | ||
for (const key of stateParams) { | ||
const value = mapUrlParam(host[key]).toString(); | ||
acc[key] = | ||
params[key] = | ||
value !== undefined && host[key] !== hybrids[key] | ||
? String(value) | ||
: undefined; | ||
return acc; | ||
}, {}), | ||
} | ||
return params; | ||
}, | ||
(host, params, lastParams) => { | ||
@@ -381,5 +385,5 @@ if (!lastParams) return; | ||
clearParams.forEach((key) => { | ||
for (const key of clearParams) { | ||
if (params[key] === undefined) delete params[key]; | ||
}); | ||
} | ||
@@ -423,5 +427,5 @@ global.history.replaceState( | ||
Object.keys(params).forEach((key) => { | ||
for (const key of Object.keys(params)) { | ||
url.searchParams.append(key, mapUrlParam(params[key])); | ||
}); | ||
} | ||
@@ -435,6 +439,7 @@ return new URL( | ||
const params = {}; | ||
url.searchParams.forEach((value, key) => { | ||
for (const [key, value] of url.searchParams) { | ||
if (writableParams.includes(key) || metaParams.includes(key)) | ||
params[key] = value; | ||
}); | ||
} | ||
@@ -451,10 +456,9 @@ return params; | ||
getEntry(params = {}, other) { | ||
const entryParams = Object.keys(params).reduce((acc, key) => { | ||
let entryParams = {}; | ||
for (const key of Object.keys(params)) { | ||
if (writableParams.includes(key)) { | ||
acc[key] = params[key]; | ||
entryParams[key] = params[key]; | ||
} | ||
} | ||
return acc; | ||
}, {}); | ||
const entry = { id, params: entryParams, ...other }; | ||
@@ -475,7 +479,7 @@ const guardConfig = config.parentsWithGuards.find((c) => !c.guard()); | ||
metaParams.forEach((key) => { | ||
for (const key of metaParams) { | ||
if (hasOwnProperty.call(params, key)) { | ||
entry.params[key] = params[key]; | ||
} | ||
}); | ||
} | ||
@@ -721,5 +725,3 @@ return entry; | ||
if (promise === activePromise) { | ||
global.requestAnimationFrame(() => { | ||
handleNavigate(pseudoEvent); | ||
}); | ||
handleNavigate(pseudoEvent); | ||
activePromise = null; | ||
@@ -732,3 +734,5 @@ } | ||
let stack = stacks.get(host); | ||
const reducedState = state.reduce((acc, entry, index) => { | ||
const reducedState = []; | ||
for (const [index, entry] of state.entries()) { | ||
if ( | ||
@@ -739,6 +743,6 @@ index === 0 || | ||
) { | ||
acc.push(entry); | ||
reducedState.push(entry); | ||
} | ||
return acc; | ||
}, []); | ||
} | ||
const offset = stack.length - reducedState.length; | ||
@@ -763,4 +767,2 @@ | ||
if (nextView === prevView) { | ||
cache.unsuspend(nextView); | ||
if (offset === 0 && host === rootRouter && entry.params.scrollToTop) { | ||
@@ -780,9 +782,9 @@ restoreLayout(nextView); | ||
Object.entries(state[0].params).forEach(([key, value]) => { | ||
for (const [key, value] of Object.entries(state[0].params)) { | ||
if (key in view) view[key] = value; | ||
}); | ||
} | ||
options.params.forEach((key) => { | ||
for (const key of options.params) { | ||
if (key in view) view[key] = host[key]; | ||
}); | ||
} | ||
@@ -793,15 +795,13 @@ if (flush) flush(); | ||
function getEntryOffset(entry) { | ||
const state = global.history.state.reduce((acc, e, index) => { | ||
const state = []; | ||
for (let [index, e] of global.history.state.entries()) { | ||
let i = 0; | ||
while (e) { | ||
acc[i] = acc[i] || []; | ||
acc[i][index] = e; | ||
state[i] = state[i] || []; | ||
state[i][index] = e; | ||
e = e.nested; | ||
i += 1; | ||
} | ||
} | ||
return acc; | ||
}, []); | ||
let offset = 0; | ||
@@ -878,3 +878,3 @@ let i = 0; | ||
global.requestAnimationFrame(restoreScrollRestoration); | ||
deferred.then(restoreScrollRestoration); | ||
} | ||
@@ -938,7 +938,2 @@ | ||
while (stack && stack[0]) { | ||
cache.suspend(stack[0]); | ||
stack = stacks.get(stack[0]); | ||
} | ||
global.history.scrollRestoration = "manual"; | ||
@@ -974,6 +969,6 @@ global.history.pushState([entry, ...state], "", url); | ||
const state = global.history.state; | ||
const bootstrapURL = new URL(global.location.href); | ||
if (!state) { | ||
const entry = | ||
getEntryFromURL(new URL(global.location.href)) || roots[0].getEntry(); | ||
const entry = getEntryFromURL(bootstrapURL) || roots[0].getEntry(); | ||
@@ -1034,2 +1029,8 @@ global.history.replaceState([entry], "", options.url); | ||
rootRouter = null; | ||
const length = global.history.state && global.history.state.length; | ||
if (length > 1) { | ||
global.history.go(1 - length); | ||
global.history.replaceState(state, "", bootstrapURL); | ||
} | ||
}; | ||
@@ -1086,3 +1087,3 @@ } | ||
connect: (host, key, invalidate) => { | ||
options.params.forEach((param) => { | ||
for (const param of options.params) { | ||
if (!(param in host)) { | ||
@@ -1093,3 +1094,3 @@ throw Error( | ||
} | ||
}); | ||
} | ||
@@ -1128,5 +1129,5 @@ if (!stacks.has(host)) stacks.set(host, []); | ||
Object.entries(entry.params).forEach(([k, v]) => | ||
console.log(`%c${k}:`, "font-weight: bold", v), | ||
); | ||
for (const [k, v] of Object.entries(entry.params)) { | ||
console.log(`%c${k}:`, "font-weight: bold", v); | ||
} | ||
@@ -1133,0 +1134,0 @@ console.groupEnd(); |
120
src/store.js
@@ -1,5 +0,4 @@ | ||
/* eslint-disable no-use-before-define */ | ||
import global from "./global.js"; | ||
import * as cache from "./cache.js"; | ||
import { storePointer } from "./utils.js"; | ||
import { storePointer, deferred } from "./utils.js"; | ||
@@ -63,3 +62,3 @@ const connect = Symbol("store.connect"); | ||
currentTimestamp = Date.now(); | ||
global.requestAnimationFrame(() => { | ||
deferred.then(() => { | ||
currentTimestamp = undefined; | ||
@@ -97,3 +96,2 @@ }); | ||
Array.from(str).reduce( | ||
// eslint-disable-next-line no-bitwise | ||
(s, c) => (Math.imul(31, s) + c.charCodeAt(0)) | 0, | ||
@@ -120,4 +118,4 @@ 0, | ||
Object.keys(previousKeys).forEach((k) => { | ||
/* istanbul ignore next */ | ||
/* istanbul ignore next */ | ||
for (const k of Object.keys(previousKeys)) { | ||
if (!offlineKeys[k] && previousKeys[k] < timestamp) { | ||
@@ -127,3 +125,3 @@ global.localStorage.removeItem(k); | ||
} | ||
}); | ||
} | ||
@@ -270,7 +268,7 @@ global.localStorage.setItem( | ||
Object.keys(items).forEach((key) => { | ||
for (const key of Object.keys(items)) { | ||
if (items[key][0] + threshold < timestamp) { | ||
delete items[key]; | ||
} | ||
}); | ||
} | ||
@@ -307,7 +305,7 @@ global.localStorage.setItem(offlineKey, JSON.stringify(items)); | ||
return cache.getEntries(config).reduce((acc, { key, value }) => { | ||
if (key === config) return acc; | ||
if (value && !error(value)) acc.push(key); | ||
return acc; | ||
}, []); | ||
const result = []; | ||
for (const { key, value } of cache.getEntries(config)) { | ||
if (key !== config && value && !error(value)) result.push(key); | ||
} | ||
return result; | ||
}, | ||
@@ -365,4 +363,3 @@ loose: true, | ||
return temp | ||
? // eslint-disable-next-line no-bitwise, no-mixed-operators | ||
(temp ^ ((Math.random() * 16) >> (temp / 4))).toString(16) | ||
? (temp ^ ((Math.random() * 16) >> (temp / 4))).toString(16) | ||
: ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid); | ||
@@ -453,7 +450,9 @@ } | ||
const proto = { | ||
toString() { | ||
return this.id || undefined; | ||
const proto = {}; | ||
Object.defineProperty(proto, "toString", { | ||
value: function () { | ||
return this.id; | ||
}, | ||
}; | ||
}); | ||
const placeholder = Object.create(proto); | ||
@@ -507,3 +506,3 @@ | ||
throw TypeError( | ||
"The 'id' property in the model definition must be set to 'true' or not defined", | ||
"The 'id' property in the model definition must be set to 'true' or not be defined", | ||
); | ||
@@ -730,6 +729,6 @@ } | ||
const model = transform.reduce((acc, fn) => { | ||
fn(acc, data, lastModel); | ||
return acc; | ||
}, Object.create(proto)); | ||
const model = Object.create(proto); | ||
for (const fn of transform) { | ||
fn(model, data, lastModel); | ||
} | ||
@@ -826,3 +825,4 @@ definitions.set(model, config); | ||
const result = items.reduce((acc, data) => { | ||
const result = []; | ||
for (const data of items) { | ||
let id = data; | ||
@@ -853,3 +853,3 @@ if (typeof data === "object" && data !== null) { | ||
if (!modelConfig.enumerable) { | ||
acc.push(model); | ||
result.push(model); | ||
} | ||
@@ -860,4 +860,4 @@ } else if (!modelConfig.enumerable) { | ||
if (modelConfig.enumerable) { | ||
const key = acc.length; | ||
Object.defineProperty(acc, key, { | ||
const key = result.length; | ||
Object.defineProperty(result, key, { | ||
get() { | ||
@@ -869,6 +869,12 @@ return cache.get(this, key, () => get(Model, id)); | ||
} | ||
return acc; | ||
}, []); | ||
} | ||
Object.defineProperty(result, "id", { value: items.id }); | ||
Object.defineProperties(result, { | ||
id: { value: items.id }, | ||
toString: { | ||
value: function () { | ||
return this.id; | ||
}, | ||
}, | ||
}); | ||
@@ -928,18 +934,16 @@ definitions.set(result, config); | ||
switch (typeof id) { | ||
case "object": | ||
return JSON.stringify( | ||
Object.keys(id) | ||
.sort() | ||
.reduce((acc, key) => { | ||
if (typeof id[key] === "object" && id[key] !== null) { | ||
throw TypeError( | ||
`You must use primitive value for '${key}' key: ${typeof id[ | ||
key | ||
]}`, | ||
); | ||
} | ||
acc[key] = id[key]; | ||
return acc; | ||
}, {}), | ||
); | ||
case "object": { | ||
const result = {}; | ||
for (const key of Object.keys(id).sort()) { | ||
if (typeof id[key] === "object" && id[key] !== null) { | ||
throw TypeError( | ||
`You must use primitive value for '${key}' key: ${typeof id[key]}`, | ||
); | ||
} | ||
result[key] = id[key]; | ||
} | ||
return JSON.stringify(result); | ||
} | ||
case "undefined": | ||
@@ -969,3 +973,2 @@ return undefined; | ||
if (suppressLog !== false && !notFoundErrors.has(err)) { | ||
// eslint-disable-next-line no-console | ||
console.error(err); | ||
@@ -1004,3 +1007,2 @@ } | ||
entry.resolved = false; | ||
entry.depState = 0; | ||
} | ||
@@ -1013,3 +1015,3 @@ | ||
if (config.contexts) { | ||
config.contexts.forEach((context) => { | ||
for (const context of config.contexts) { | ||
if ( | ||
@@ -1021,3 +1023,3 @@ cache.get(context, context, resolveTimestamp) === | ||
} | ||
}); | ||
} | ||
} | ||
@@ -1169,3 +1171,3 @@ | ||
if (localModel) { | ||
config.checks.forEach((fn, key) => { | ||
for (const [key, fn] of config.checks.entries()) { | ||
if (keys.indexOf(key) === -1) { | ||
@@ -1177,5 +1179,4 @@ if (lastError && lastError.errors && lastError.errors[key]) { | ||
// eslint-disable-next-line eqeqeq | ||
if (isDraft && localModel[key] == config.model[key]) { | ||
return; | ||
continue; | ||
} | ||
@@ -1195,3 +1196,3 @@ } | ||
} | ||
}); | ||
} | ||
@@ -1335,6 +1336,6 @@ if (hasErrors && !isDraft) { | ||
cache.getEntries(config).forEach((entry) => { | ||
for (const entry of cache.getEntries(config)) { | ||
if (offline) offline.set(entry.key, null); | ||
if (entry.value) invalidateTimestamp(entry.value); | ||
}); | ||
} | ||
cache.invalidateAll(config, { clearValue, deleteEntry: true }); | ||
@@ -1453,11 +1454,8 @@ } | ||
case "string": | ||
// eslint-disable-next-line no-new-wrappers | ||
defaultValue = new String(defaultValue); | ||
break; | ||
case "number": | ||
// eslint-disable-next-line no-new-wrappers | ||
defaultValue = new Number(defaultValue); | ||
break; | ||
case "boolean": | ||
// eslint-disable-next-line no-new-wrappers | ||
defaultValue = new Boolean(defaultValue); | ||
@@ -1464,0 +1462,0 @@ break; |
@@ -22,18 +22,18 @@ import global from "../global.js"; | ||
function createSignature(parts) { | ||
let signature = parts.reduce((acc, part, index) => { | ||
if (index === 0) { | ||
return part; | ||
} | ||
let signature = parts[0]; | ||
let tableMode = false; | ||
for (let index = 1; index < parts.length; index += 1) { | ||
tableMode = | ||
tableMode || | ||
parts[index - 1].match(/<\s*(table|tr|thead|tbody|tfoot|colgroup)>\s*$/); | ||
if ( | ||
parts | ||
.slice(index) | ||
.join("") | ||
.match(/^\s*<\/\s*(table|tr|thead|tbody|tfoot|colgroup)>/) | ||
) { | ||
return `${acc}<!--${getPlaceholder(index - 1)}-->${part}`; | ||
} | ||
signature += | ||
(tableMode | ||
? `<!--${getPlaceholder(index - 1)}-->` | ||
: getPlaceholder(index - 1)) + parts[index]; | ||
return acc + getPlaceholder(index - 1) + part; | ||
}, ""); | ||
tableMode = | ||
tableMode && | ||
!parts[index].match(/<\/\s*(table|tr|thead|tbody|tfoot|colgroup)>/); | ||
} | ||
@@ -53,3 +53,2 @@ return signature; | ||
context, | ||
// eslint-disable-next-line no-bitwise | ||
global.NodeFilter.SHOW_ELEMENT | | ||
@@ -96,3 +95,2 @@ global.NodeFilter.SHOW_TEXT | | ||
.join("\n") | ||
// eslint-disable-next-line no-template-curly-in-string | ||
.replace(PLACEHOLDER_REGEXP_ALL, "${...}"); | ||
@@ -183,5 +181,5 @@ | ||
template.content.removeChild(svgRoot); | ||
Array.from(svgRoot.childNodes).forEach((node) => | ||
template.content.appendChild(node), | ||
); | ||
for (const node of Array.from(svgRoot.childNodes)) { | ||
template.content.appendChild(node); | ||
} | ||
} | ||
@@ -192,3 +190,3 @@ | ||
if (layoutTemplate instanceof global.HTMLTemplateElement) { | ||
Array.from(layoutTemplate.attributes).forEach((attr) => { | ||
for (const attr of Array.from(layoutTemplate.attributes)) { | ||
const value = attr.value.trim(); | ||
@@ -207,3 +205,3 @@ if (attr.name.startsWith("layout") && value) { | ||
} | ||
}); | ||
} | ||
@@ -331,3 +329,3 @@ if (hostLayout !== undefined && template.content.children.length > 1) { | ||
} else { | ||
/* istanbul ignore else */ // eslint-disable-next-line no-lonely-if | ||
/* istanbul ignore else */ | ||
if (node.nodeType === global.Node.ELEMENT_NODE) { | ||
@@ -343,3 +341,3 @@ if ( | ||
/* istanbul ignore else */ // eslint-disable-next-line no-lonely-if | ||
/* istanbul ignore else */ | ||
if (probablyDevMode) { | ||
@@ -356,3 +354,3 @@ const tagName = node.tagName.toLowerCase(); | ||
Array.from(node.attributes).forEach((attr) => { | ||
for (const attr of Array.from(node.attributes)) { | ||
const value = attr.value.trim(); | ||
@@ -371,3 +369,3 @@ /* istanbul ignore next */ | ||
return; | ||
continue; | ||
} | ||
@@ -388,3 +386,3 @@ | ||
results.forEach((placeholder, index) => { | ||
for (const [index, placeholder] of results.entries()) { | ||
const [, id] = placeholder.match(PLACEHOLDER_REGEXP_EQUAL); | ||
@@ -416,3 +414,3 @@ let isProp = false; | ||
]; | ||
}); | ||
} | ||
@@ -422,3 +420,3 @@ attr.value = ""; | ||
} | ||
}); | ||
} | ||
} | ||
@@ -508,8 +506,7 @@ } | ||
meta.markers.forEach((marker) => { | ||
for (const marker of meta.markers) { | ||
const value = args[marker.index]; | ||
const prevValue = meta.prevArgs && meta.prevArgs[marker.index]; | ||
// eslint-disable-next-line no-continue | ||
if (meta.prevArgs && value === prevValue) return; | ||
if (meta.prevArgs && value === prevValue) continue; | ||
@@ -519,3 +516,2 @@ try { | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.error( | ||
@@ -529,3 +525,3 @@ `Following error was thrown when updating a template expression in ${stringifyElement( | ||
} | ||
}); | ||
} | ||
@@ -532,0 +528,0 @@ meta.prevArgs = args; |
@@ -1,2 +0,1 @@ | ||
import global from "../global.js"; | ||
import { storePointer } from "../utils.js"; | ||
@@ -37,3 +36,2 @@ import resolveTemplateValue from "./resolvers/value.js"; | ||
const stringCache = new Map(); | ||
const storeValues = new WeakMap(); | ||
@@ -68,19 +66,3 @@ export function set(property, valueOrPath) { | ||
resolveValue(event, (value) => { | ||
const values = storeValues.get(property); | ||
if (!values) { | ||
global.requestAnimationFrame(() => { | ||
const result = storeValues.get(property); | ||
storeValues.delete(property); | ||
store | ||
.set(property, result) | ||
.catch(/* istanbul ignore next */ () => {}); | ||
}); | ||
} | ||
storeValues.set(property, { | ||
...values, | ||
...getPartialObject(valueOrPath, value), | ||
}); | ||
store.set(property, getPartialObject(valueOrPath, value)); | ||
}); | ||
@@ -119,6 +101,3 @@ }; | ||
timeout = undefined; | ||
global.requestAnimationFrame(() => { | ||
placeholder(host, target); | ||
}); | ||
resolveTemplateValue(host, target, placeholder); | ||
}, delay); | ||
@@ -125,0 +104,0 @@ } |
@@ -24,10 +24,10 @@ import { compileTemplate } from "./core.js"; | ||
this.styleSheets.push( | ||
parts.reduce( | ||
(acc, part, index) => | ||
`${acc}${part}${args[index] !== undefined ? args[index] : ""}`, | ||
"", | ||
), | ||
); | ||
let result = parts[0]; | ||
for (let index = 1; index < parts.length; index++) { | ||
result += | ||
(args[index - 1] !== undefined ? args[index - 1] : "") + parts[index]; | ||
} | ||
this.styleSheets.push(result); | ||
return this; | ||
@@ -34,0 +34,0 @@ }, |
@@ -42,3 +42,5 @@ import global from "../../global.js"; | ||
const ids = new Set(); | ||
entries.forEach((entry) => ids.add(entry.id)); | ||
for (const entry of entries) { | ||
ids.add(entry.id); | ||
} | ||
@@ -106,3 +108,3 @@ lastEntries = lastEntries.filter((entry) => { | ||
if (lastEntries) { | ||
lastEntries.forEach((entry) => { | ||
for (const entry of lastEntries) { | ||
if (entry.available) { | ||
@@ -112,4 +114,4 @@ removeTemplate(entry.placeholder); | ||
} | ||
}); | ||
} | ||
} | ||
} |
function normalizeValue(value, set = new Set()) { | ||
if (Array.isArray(value)) { | ||
value.forEach((className) => set.add(className)); | ||
for (const className of value) { | ||
if (className) set.add(className); | ||
} | ||
} else if (value !== null && typeof value === "object") { | ||
Object.keys(value).forEach((key) => value[key] && set.add(key)); | ||
for (const [className, condition] of Object.entries(value)) { | ||
if (className && condition) set.add(className); | ||
} | ||
} else { | ||
set.add(value); | ||
if (value) set.add(value); | ||
} | ||
@@ -21,10 +25,10 @@ | ||
list.forEach((className) => { | ||
for (const className of list) { | ||
target.classList.add(className); | ||
previousList.delete(className); | ||
}); | ||
} | ||
previousList.forEach((className) => { | ||
for (const className of previousList) { | ||
target.classList.remove(className); | ||
}); | ||
} | ||
} |
@@ -15,3 +15,5 @@ import { camelToDash, stringifyElement } from "../../utils.js"; | ||
const nextMap = Object.keys(value).reduce((map, key) => { | ||
const nextMap = new Map(); | ||
for (const key of Object.keys(value)) { | ||
const dashKey = camelToDash(key); | ||
@@ -26,13 +28,11 @@ const styleValue = value[key]; | ||
map.set(dashKey, styleValue); | ||
nextMap.set(dashKey, styleValue); | ||
previousMap.delete(dashKey); | ||
} | ||
return map; | ||
}, new Map()); | ||
previousMap.forEach((styleValue, key) => { | ||
for (const key of previousMap.keys()) { | ||
target.style[key] = ""; | ||
}); | ||
} | ||
styleMap.set(target, nextMap); | ||
} |
12
63
443436
6610