fix(scoped-elements): support extending elements (#1272)

This commit defers the definition of web components until they are used, so it allows to extend parent components without define unused inherited web components.
This commit is contained in:
Manuel Martin
2020-02-13 14:32:04 +01:00
committed by Thomas Allmer
parent 8855c88164
commit 9868bc7a1b
6 changed files with 52 additions and 59 deletions

View File

@@ -2,8 +2,8 @@
import { createUniqueTag } from './tag.js';
const tagsCache = new Map();
const registerElement = (klass, tag) => tagsCache.set(klass, tag);
const getRegisteredTag = klass => tagsCache.get(klass);
const addToTagsCache = (klass, tag) => tagsCache.set(klass, tag);
const getFromTagsCache = klass => tagsCache.get(klass);
const defineElement = (tagName, klass) => {
const registry = customElements;
@@ -11,15 +11,10 @@ const defineElement = (tagName, klass) => {
// we extend it just in case the class has been defined manually
registry.define(tag, class extends klass {});
registerElement(klass, tag);
addToTagsCache(klass, tag);
return tag;
};
export const registerElements = elements =>
Object.keys(elements).reduce((acc, tagName) => {
const klass = elements[tagName];
acc[tagName] = getRegisteredTag(klass) || defineElement(tagName, klass);
return acc;
}, {});
export const registerElement = (tagName, klass) =>
getFromTagsCache(klass) || defineElement(tagName, klass);

View File

@@ -1,18 +1,14 @@
/* eslint-disable prefer-spread */
import { html } from 'lit-html';
import { transform } from './transform.js';
import { registerElements } from './scoped-elements.js';
export const createScopedHtml = tags => {
const definedTags = registerElements(tags);
export const createScopedHtml = tags =>
// eslint-disable-next-line func-names
return function() {
function() {
// eslint-disable-next-line prefer-rest-params
const args = Array.apply(null, arguments);
const strings = args[0];
const values = args.slice(1);
return html.apply(null, [].concat([transform(strings, definedTags)], values));
return html.apply(null, [].concat([transform(strings, tags)], values));
};
};

View File

@@ -1,4 +1,5 @@
import { fromCache, toCache } from './cache.js';
import { registerElement } from './scoped-elements.js';
const re = /<\/?([a-zA-Z0-9-]+)/g;
@@ -22,13 +23,14 @@ const transformTemplate = (strings, tags) =>
for (let i = matches.length - 1; i >= 0; i -= 1) {
const item = matches[i];
const replacement = tags[item[1]];
const klass = tags[item[1]];
if (replacement) {
if (klass) {
const tag = registerElement(item[1], klass);
const start = item.index + item[0].length - item[1].length;
const end = start + item[1].length;
acc = acc.slice(0, start) + replacement + acc.slice(end);
acc = acc.slice(0, start) + tag + acc.slice(end);
}
}

View File

@@ -1,5 +1,5 @@
import { expect } from '@open-wc/testing';
import { registerElements } from '../src/scoped-elements.js';
import { registerElement } from '../src/scoped-elements.js';
import { SUFFIX } from '../src/tag.js';
describe('scoped-elements', () => {
@@ -9,17 +9,13 @@ describe('scoped-elements', () => {
class Alderaan extends HTMLElement {}
class Bespin extends HTMLElement {}
const result = registerElements({
'naboo-planet': Naboo,
'alderaan-planet': Alderaan,
'bespin-planet': Bespin,
});
const nabooTag = registerElement('naboo-planet', Naboo);
const alderaanTag = registerElement('alderaan-planet', Alderaan);
const bespinTag = registerElement('bespin-planet', Bespin);
expect(result).to.deep.equal({
'naboo-planet': `naboo-planet-${SUFFIX}`,
'alderaan-planet': `alderaan-planet-${SUFFIX}`,
'bespin-planet': `bespin-planet-${SUFFIX}`,
});
expect(nabooTag).to.equal(`naboo-planet-${SUFFIX}`);
expect(alderaanTag).to.equal(`alderaan-planet-${SUFFIX}`);
expect(bespinTag).to.equal(`bespin-planet-${SUFFIX}`);
});
});
});

View File

@@ -27,4 +27,21 @@ describe('createScopedHtml', () => {
'\n <!---->',
);
});
it('defers the definition of components until they are used', () => {
class Ithor extends HTMLElement {}
class Taris extends HTMLElement {}
const html = createScopedHtml({
'ithor-planet': Ithor,
'taris-planet': Taris,
});
html`
<taris-planet></taris-planet>
`;
expect(customElements.get(`ithor-planet-${SUFFIX}`)).to.be.undefined;
expect(customElements.get(`taris-planet-${SUFFIX}`)).to.not.be.undefined;
});
});

View File

@@ -1,46 +1,33 @@
import { expect } from '@open-wc/testing';
import { transform } from '../src/transform.js';
const tags = {
'mandalore-planet': class extends HTMLElement {},
};
describe('html', () => {
[
{
input: ['<naboo-planet>', '</naboo-planet>'],
tags: { 'naboo-planet': 'c-naboo' },
output: ['<c-naboo>', '</c-naboo>'],
input: ['<mandalore-planet>', '</mandalore-planet>'],
output: ['<mandalore-planet-se>', '</mandalore-planet-se>'],
},
{
input: ['<naboo-planet class="sample">', '</naboo-planet>'],
tags: { 'naboo-planet': 'c-naboo' },
output: ['<c-naboo class="sample">', '</c-naboo>'],
input: ['<mandalore-planet class="sample">', '</mandalore-planet>'],
output: ['<mandalore-planet-se class="sample">', '</mandalore-planet-se>'],
},
{
input: ['<naboo-planet\tclass="sample">', '</naboo-planet>'],
tags: { 'naboo-planet': 'c-naboo' },
output: ['<c-naboo\tclass="sample">', '</c-naboo>'],
input: ['<mandalore-planet\tclass="sample">', '</mandalore-planet>'],
output: ['<mandalore-planet-se\tclass="sample">', '</mandalore-planet-se>'],
},
{
input: ['<naboo-planet\rclass="sample">', '</naboo-planet>'],
tags: { 'naboo-planet': 'c-naboo' },
output: ['<c-naboo\rclass="sample">', '</c-naboo>'],
input: ['<mandalore-planet\rclass="sample">', '</mandalore-planet>'],
output: ['<mandalore-planet-se\rclass="sample">', '</mandalore-planet-se>'],
},
{
input: ['<naboo-planet class="sample"></naboo-planet>'],
tags: { 'naboo-planet': 'c-naboo' },
output: ['<c-naboo class="sample"></c-naboo>'],
input: ['<mandalore-planet class="sample"></mandalore-planet>'],
output: ['<mandalore-planet-se class="sample"></mandalore-planet-se>'],
},
{
input: [
'<naboo-planet class="sample">',
'</naboo-planet><bespin-planet>',
'</bespin-planet>',
],
tags: {
'naboo-planet': 'c-naboo',
'bespin-planet': 'c-bespin',
},
output: ['<c-naboo class="sample">', '</c-naboo><c-bespin>', '</c-bespin>'],
},
].forEach(({ input, tags, output }, index) => {
].forEach(({ input, output }, index) => {
it(`should transform strings tags into the actual registered tags - ${index}`, () => {
expect(transform(input, tags)).to.be.deep.equal(output);
});