Skip to content

Commit

Permalink
Merge b7fdc91 into e8a4ded
Browse files Browse the repository at this point in the history
  • Loading branch information
smalluban committed Jan 27, 2020
2 parents e8a4ded + b7fdc91 commit e590a71
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 33 deletions.
33 changes: 19 additions & 14 deletions src/cache.js
Expand Up @@ -49,7 +49,6 @@ export function get(target, key, getter) {
const entry = getEntry(target, key);

if (contextStack.size && contextStack.has(entry)) {
contextStack.clear();
throw Error(`Circular get invocation of the '${key}' property in '${stringifyElement(target)}'`);
}

Expand All @@ -63,21 +62,20 @@ export function get(target, key, getter) {
}
});

contextStack.add(entry);

if (entry.checksum && entry.checksum === calculateChecksum(entry)) {
contextStack.delete(entry);
return entry.value;
}

if (entry.deps && entry.deps.size) {
entry.deps.forEach((depEntry) => {
if (depEntry.contexts) depEntry.contexts.delete(entry);
});
entry.deps = undefined;
}

try {
contextStack.add(entry);

if (entry.observed && entry.deps && entry.deps.size) {
entry.deps.forEach((depEntry) => {
depEntry.contexts.delete(entry);
});
}

entry.deps = undefined;
const nextValue = getter(target, entry.value);

if (nextValue !== entry.value) {
Expand All @@ -90,7 +88,16 @@ export function get(target, key, getter) {
entry.checksum = calculateChecksum(entry);
contextStack.delete(entry);
} catch (e) {
contextStack.clear();
entry.checksum = 0;
entry.state += 1;

contextStack.delete(entry);

contextStack.forEach((context) => {
context.deps.delete(entry);
if (context.observed) entry.contexts.delete(context);
});

throw e;
}

Expand All @@ -99,7 +106,6 @@ export function get(target, key, getter) {

export function set(target, key, setter, value, force) {
if (contextStack.size && !force) {
contextStack.clear();
throw Error(`Try to set '${key}' of '${stringifyElement(target)}' in get call`);
}

Expand All @@ -117,7 +123,6 @@ export function set(target, key, setter, value, force) {

export function invalidate(target, key, clearValue) {
if (contextStack.size) {
contextStack.clear();
throw Error(`Try to invalidate '${key}' in '${stringifyElement(target)}' get call`);
}

Expand Down
2 changes: 1 addition & 1 deletion src/define.js
Expand Up @@ -82,7 +82,7 @@ if (process.env.NODE_ENV !== 'production') {
node.disconnectedCallback();

Object.keys(node.constructor.hybrids).forEach((key) => {
cache.invalidate(node, key, node[key] === hybrids[key]);
cache.invalidate(node, key, node.constructor.hybrids[key] !== hybrids[key]);
});

node.connectedCallback();
Expand Down
13 changes: 7 additions & 6 deletions src/template/core.js
Expand Up @@ -250,7 +250,7 @@ export function compileTemplate(rawParts, isSVG, styles) {

if (template !== data.template) {
if (data.template || target.nodeType === Node.ELEMENT_NODE) removeTemplate(target);
data.lastArgs = null;
data.prevArgs = null;

const fragment = document.importNode(applyShadyCSS(template, host.tagName).content, true);

Expand Down Expand Up @@ -306,25 +306,26 @@ export function compileTemplate(rawParts, isSVG, styles) {
}
}

const prevArgs = data.prevArgs;
data.prevArgs = args;

for (let index = 0; index < data.markers.length; index += 1) {
const [node, marker] = data.markers[index];
if (!data.lastArgs || data.lastArgs[index] !== args[index]) {
marker(host, node, args[index], data.lastArgs ? data.lastArgs[index] : undefined);
if (!prevArgs || prevArgs[index] !== args[index]) {
marker(host, node, args[index], prevArgs ? prevArgs[index] : undefined);
}
}

if (target.nodeType !== Node.TEXT_NODE) {
shadyCSS((shady) => {
if (host.shadowRoot) {
if (data.lastArgs) {
if (prevArgs) {
shady.styleSubtree(host);
} else {
shady.styleElement(host);
}
}
});
}

data.lastArgs = args;
};
}
26 changes: 18 additions & 8 deletions src/template/resolvers/event.js
@@ -1,25 +1,35 @@
const eventMap = new WeakMap();
const targets = new WeakMap();

export default function resolveEventListener(eventType) {
return (host, target, value, lastValue) => {
if (lastValue) {
target.removeEventListener(
eventType,
eventMap.get(lastValue),
lastValue.options !== undefined ? lastValue.options : false,
);
const eventMap = targets.get(target);
if (eventMap) {
target.removeEventListener(
eventType,
eventMap.get(lastValue),
lastValue.options !== undefined ? lastValue.options : false,
);
}
}

if (value) {
if (typeof value !== 'function') {
throw Error(`Event listener must be a function: ${typeof value}`);
}

eventMap.set(value, value.bind(null, host));
let eventMap = targets.get(target);
if (!eventMap) {
eventMap = new WeakMap();
targets.set(target, eventMap);
}

const callback = value.bind(null, host);
eventMap.set(value, callback);

target.addEventListener(
eventType,
eventMap.get(value),
callback,
value.options !== undefined ? value.options : false,
);
}
Expand Down
16 changes: 14 additions & 2 deletions test/spec/cache.js
Expand Up @@ -152,7 +152,7 @@ describe('cache:', () => {
});
});

it('clean emitter when unobserve', (done) => {
it('cleans emitter when unobserve', (done) => {
const unobserve = observe(target, 'key', _, spy);

requestAnimationFrame(() => {
Expand All @@ -165,7 +165,7 @@ describe('cache:', () => {
});
});

it('clean dependencies contexts when unobserve', (done) => {
it('cleans dependencies contexts when unobserve', (done) => {
const getter = () => get(target, 'otherKey', () => get(target, 'deepKey', _));
const unobserve = observe(target, 'key', getter, spy);

Expand All @@ -179,5 +179,17 @@ describe('cache:', () => {
});
});
});

it('cleans contexts when getter throws', (done) => {
const getKey = () => get(target, 'key', () => get(target, 'otherKey', () => { throw Error(); }));
const unobserve = observe(target, 'key', () => {}, spy);

expect(() => getKey()).toThrow();

requestAnimationFrame(() => {
expect(() => unobserve()).not.toThrow();
done();
});
});
});
});
7 changes: 5 additions & 2 deletions test/spec/html.js
Expand Up @@ -239,7 +239,7 @@ describe('html:', () => {
});

describe('event attribute expression', () => {
const render = (value) => html`<button onclick=${value}></button>`;
const render = (value) => html`<button onclick=${value}></button><button onclick=${value}></button>`;
const renderWithQuotes = (value) => html`<button onclick="${value}"></button>`;

const click = () => fragment.children[0].click();
Expand All @@ -249,10 +249,13 @@ describe('html:', () => {
spy = jasmine.createSpy('event callback');
});

it('throws for other type than function', () => {
it('throws for other type than function and attaches event after', () => {
expect(() => {
render({})(fragment);
}).toThrow();
render(spy)(fragment);
click();
expect(spy).toHaveBeenCalledTimes(1);
});

it('attaches event listener', () => {
Expand Down

0 comments on commit e590a71

Please sign in to comment.