",
5 | "homepage": "https://github.com/jahilldev/component-elements/tree/main/packages/preactement#readme",
6 | "license": "MIT",
7 | "main": "./dist/define.es5.js",
8 | "types": "./dist/define.d.ts",
9 | "engines": {
10 | "node": ">=10"
11 | },
12 | "exports": {
13 | ".": "./dist/define.js",
14 | "./es5": {
15 | "type": "./dist/define.d.ts",
16 | "import": "./dist/define.es5.js",
17 | "require": "./dist/define.es5.js"
18 | }
19 | },
20 | "typesVersions": {
21 | "*": {
22 | "es5": [
23 | "./dist/define.d.ts"
24 | ]
25 | }
26 | },
27 | "keywords": [
28 | "preact",
29 | "custom elements",
30 | "web components",
31 | "virtual dom",
32 | "partial hydration",
33 | "universal",
34 | "isomorphic",
35 | "hydrate",
36 | "component"
37 | ],
38 | "directories": {
39 | "lib": "dist",
40 | "test": "tests"
41 | },
42 | "files": [
43 | "dist"
44 | ],
45 | "repository": {
46 | "type": "git",
47 | "url": "git+https://github.com/jahilldev/component-elements.git"
48 | },
49 | "scripts": {
50 | "start": "run-s clean watch",
51 | "build": "webpack --mode=production",
52 | "watch": "webpack --watch",
53 | "clean": "rimraf ./dist",
54 | "lint": "eslint",
55 | "test": "jest"
56 | },
57 | "bugs": {
58 | "url": "https://github.com/jahilldev/component-elements/issues"
59 | },
60 | "peerDependencies": {
61 | "preact": "10.x"
62 | },
63 | "devDependencies": {
64 | "@component-elements/shared": "1.0.0",
65 | "@types/enzyme": "^3.10.12",
66 | "enzyme": "^3.11.0",
67 | "enzyme-adapter-preact-pure": "^4.1.0",
68 | "preact": "^10.11.3"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/packages/preactement/src/define.ts:
--------------------------------------------------------------------------------
1 | import { h, render, ComponentFactory, FunctionComponent } from 'preact';
2 | import {
3 | IProps,
4 | ErrorTypes,
5 | CustomElement,
6 | isPromise,
7 | parseJson,
8 | getElementTag,
9 | getPropKey,
10 | getElementAttributes,
11 | getAsyncComponent,
12 | } from '@component-elements/shared';
13 | import { parseHtml } from './parse';
14 | import { IOptions, ComponentFunction } from './model';
15 |
16 | /* -----------------------------------
17 | *
18 | * Define
19 | *
20 | * -------------------------------- */
21 |
22 | function define(
23 | tagName: string,
24 | child: ComponentFunction
,
25 | options: IOptions = {}
26 | ): FunctionComponent
{
27 | const { wrapComponent } = options;
28 | const preRender = typeof window === 'undefined';
29 | const elementTag = getElementTag(tagName);
30 |
31 | if (!preRender) {
32 | customElements.define(elementTag, setupElement(child, options));
33 |
34 | return;
35 | }
36 |
37 | const content = child();
38 |
39 | if (isPromise(content)) {
40 | throw new Error(`${ErrorTypes.Promise} : <${tagName}>`);
41 | }
42 |
43 | let component = content;
44 | const attributes: Record = { server: true };
45 |
46 | if (wrapComponent) {
47 | component = wrapComponent(content);
48 | }
49 |
50 | return (props: P) =>
51 | h(elementTag, attributes, [
52 | h('script', {
53 | type: 'application/json',
54 | dangerouslySetInnerHTML: { __html: JSON.stringify(props) },
55 | }),
56 | h(component, props),
57 | ]);
58 | }
59 |
60 | /* -----------------------------------
61 | *
62 | * Setup
63 | *
64 | * -------------------------------- */
65 |
66 | function setupElement(component: ComponentFunction, options: IOptions = {}): any {
67 | const { attributes = [] } = options;
68 |
69 | if (typeof Reflect !== 'undefined' && Reflect.construct) {
70 | const CustomElement = function () {
71 | const element = Reflect.construct(HTMLElement, [], CustomElement);
72 |
73 | element.__mounted = false;
74 | element.__component = component;
75 | element.__properties = {};
76 | element.__slots = {};
77 | element.__children = void 0;
78 | element.__options = options;
79 |
80 | return element;
81 | };
82 |
83 | CustomElement.observedAttributes = ['props', ...attributes];
84 |
85 | CustomElement.prototype = Object.create(HTMLElement.prototype);
86 | CustomElement.prototype.constructor = CustomElement;
87 | CustomElement.prototype.connectedCallback = onConnected;
88 | CustomElement.prototype.attributeChangedCallback = onAttributeChange;
89 | CustomElement.prototype.disconnectedCallback = onDisconnected;
90 |
91 | return CustomElement;
92 | }
93 |
94 | return class CustomElement extends HTMLElement {
95 | __mounted = false;
96 | __component = component;
97 | __properties = {};
98 | __slots = {};
99 | __children = void 0;
100 | __options = options;
101 |
102 | static observedAttributes = ['props', ...attributes];
103 |
104 | public connectedCallback() {
105 | onConnected.call(this);
106 | }
107 |
108 | public attributeChangedCallback(...args) {
109 | onAttributeChange.call(this, ...args);
110 | }
111 |
112 | public disconnectedCallback() {
113 | onDisconnected.call(this);
114 | }
115 | };
116 | }
117 |
118 | /* -----------------------------------
119 | *
120 | * Connected
121 | *
122 | * -------------------------------- */
123 |
124 | function onConnected(this: CustomElement) {
125 | const attributes = getElementAttributes.call(this);
126 | const props = this.getAttribute('props');
127 | const json = this.querySelector('[type="application/json"]');
128 | const data = parseJson.call(this, props || json?.innerHTML || '{}');
129 |
130 | json?.remove();
131 |
132 | let children = this.__children;
133 |
134 | if (!this.__mounted && !this.hasAttribute('server')) {
135 | children = h(parseHtml.call(this), {});
136 | }
137 |
138 | this.__properties = { ...this.__slots, ...data, ...attributes };
139 | this.__children = children || [];
140 |
141 | this.removeAttribute('server');
142 |
143 | const response = this.__component();
144 | const renderer = (result: ComponentFactory) => finaliseComponent.call(this, result);
145 |
146 | if (isPromise(response)) {
147 | getAsyncComponent(response, this.tagName).then(renderer);
148 |
149 | return;
150 | }
151 |
152 | renderer(response);
153 | }
154 |
155 | /* -----------------------------------
156 | *
157 | * Attribute
158 | *
159 | * -------------------------------- */
160 |
161 | function onAttributeChange(this: CustomElement, name: string, original: string, updated: string) {
162 | if (!this.__mounted) {
163 | return;
164 | }
165 |
166 | updated = updated == null ? void 0 : updated;
167 |
168 | let props = this.__properties;
169 |
170 | if (name === 'props') {
171 | props = { ...props, ...parseJson.call(this, updated) };
172 | } else {
173 | props[getPropKey(name)] = updated;
174 | }
175 |
176 | this.__properties = props;
177 |
178 | render(h(this.__instance, { ...props, parent: this, children: this.__children }), this);
179 | }
180 |
181 | /* -----------------------------------
182 | *
183 | * Disconnected
184 | *
185 | * -------------------------------- */
186 |
187 | function onDisconnected(this: CustomElement) {
188 | render(null, this);
189 | }
190 |
191 | /* -----------------------------------
192 | *
193 | * Finalise
194 | *
195 | * -------------------------------- */
196 |
197 | function finaliseComponent(this: CustomElement, component: ComponentFactory) {
198 | const { tagName } = this;
199 | const { wrapComponent } = this.__options;
200 |
201 | if (!component) {
202 | console.error(ErrorTypes.Missing, `: <${tagName.toLowerCase()}>`);
203 |
204 | return;
205 | }
206 |
207 | if (wrapComponent) {
208 | component = wrapComponent(component);
209 | }
210 |
211 | this.__instance = component;
212 | this.__mounted = true;
213 |
214 | const props = {
215 | ...this.__properties,
216 | parent: this,
217 | children: this.__children,
218 | };
219 |
220 | render(h(component, props), this);
221 | }
222 |
223 | /* -----------------------------------
224 | *
225 | * Export
226 | *
227 | * -------------------------------- */
228 |
229 | export { define };
230 |
--------------------------------------------------------------------------------
/packages/preactement/src/model.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFactory } from 'preact';
2 |
3 | /* -----------------------------------
4 | *
5 | * Types
6 | *
7 | * -------------------------------- */
8 |
9 | type ComponentFunction = () => ComponentResult
;
10 | type ComponentResult
= ComponentFactory
| ComponentAsync
;
11 | type ComponentAsync
=
12 | | Promise>
13 | | Promise<{ [index: string]: ComponentFactory }>;
14 |
15 | /* -----------------------------------
16 | *
17 | * IOptions
18 | *
19 | * -------------------------------- */
20 |
21 | interface IOptions {
22 | attributes?: string[];
23 | formatProps?:
(props: P) => P;
24 | wrapComponent?:
(child: ComponentFactory
) => ComponentFactory
;
25 | }
26 |
27 | /* -----------------------------------
28 | *
29 | * Export
30 | *
31 | * -------------------------------- */
32 |
33 | export { IOptions, ComponentFunction, ComponentResult, ComponentAsync };
34 |
--------------------------------------------------------------------------------
/packages/preactement/src/parse.ts:
--------------------------------------------------------------------------------
1 | import { h, ComponentFactory, Fragment } from 'preact';
2 | import {
3 | CustomElement,
4 | getDocument,
5 | getAttributeObject,
6 | selfClosingTags,
7 | getPropKey,
8 | } from '@component-elements/shared';
9 |
10 | /* -----------------------------------
11 | *
12 | * parseHtml
13 | *
14 | * -------------------------------- */
15 |
16 | function parseHtml(this: CustomElement): ComponentFactory<{}> {
17 | const dom = getDocument(this.innerHTML);
18 |
19 | if (!dom) {
20 | return void 0;
21 | }
22 |
23 | const result = convertToVDom.call(this, dom);
24 |
25 | return () => result;
26 | }
27 |
28 | /* -----------------------------------
29 | *
30 | * convertToVDom
31 | *
32 | * -------------------------------- */
33 |
34 | function convertToVDom(this: CustomElement, node: Element) {
35 | if (node.nodeType === 3) {
36 | return node.textContent || '';
37 | }
38 |
39 | if (node.nodeType !== 1) {
40 | return null;
41 | }
42 |
43 | const nodeName = String(node.nodeName).toLowerCase();
44 | const childNodes = Array.from(node.childNodes);
45 |
46 | const children = () => childNodes.map((child) => convertToVDom.call(this, child));
47 | const { slot, ...props } = getAttributeObject(node.attributes);
48 |
49 | if (nodeName === 'script') {
50 | return null;
51 | }
52 |
53 | if (nodeName === 'body') {
54 | return h(Fragment, {}, children());
55 | }
56 |
57 | if (selfClosingTags.includes(nodeName)) {
58 | return h(nodeName, props);
59 | }
60 |
61 | if (slot) {
62 | this.__slots[getPropKey(slot)] = getSlotChildren(children());
63 |
64 | return null;
65 | }
66 |
67 | return h(nodeName, props, children());
68 | }
69 |
70 | /* -----------------------------------
71 | *
72 | * getSlotChildren
73 | *
74 | * -------------------------------- */
75 |
76 | function getSlotChildren(children: JSX.Element[]) {
77 | const isString = (item) => typeof item === 'string';
78 |
79 | if (children.every(isString)) {
80 | return children.join(' ');
81 | }
82 |
83 | return h(Fragment, {}, children);
84 | }
85 |
86 | /* -----------------------------------
87 | *
88 | * Export
89 | *
90 | * -------------------------------- */
91 |
92 | export { parseHtml };
93 |
--------------------------------------------------------------------------------
/packages/preactement/tests/define.spec.tsx:
--------------------------------------------------------------------------------
1 | import { h, Fragment, ComponentFactory } from 'preact';
2 | import { mount } from 'enzyme';
3 | import { define } from '../src/define';
4 |
5 | /* -----------------------------------
6 | *
7 | * Promises
8 | *
9 | * -------------------------------- */
10 |
11 | function flushPromises() {
12 | return new Promise((resolve) => setTimeout(resolve, 0));
13 | }
14 |
15 | /* -----------------------------------
16 | *
17 | * IProps
18 | *
19 | * -------------------------------- */
20 |
21 | interface IProps {
22 | customTitle?: string;
23 | value: string;
24 | children?: any;
25 | }
26 |
27 | /* -----------------------------------
28 | *
29 | * Component
30 | *
31 | * -------------------------------- */
32 |
33 | function Message({ customTitle, value, children }: IProps) {
34 | return (
35 |
36 | {customTitle && {customTitle} }
37 | {value}
38 | {children}
39 |
40 | );
41 | }
42 |
43 | /* -----------------------------------
44 | *
45 | * Define
46 | *
47 | * -------------------------------- */
48 |
49 | describe('define()', () => {
50 | const { document } = globalThis;
51 | const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
52 |
53 | describe('when run in the browser', () => {
54 | let root;
55 |
56 | beforeEach(() => {
57 | root = document.createElement('div');
58 | document.body.appendChild(root);
59 | });
60 |
61 | afterEach(() => {
62 | document.body.removeChild(root);
63 | });
64 |
65 | it('validates tag name value with prefix if needed', () => {
66 | const props = { value: 'propsValue' };
67 |
68 | define('message', () => Message);
69 |
70 | const element = document.createElement('component-message');
71 |
72 | element.setAttribute('props', JSON.stringify(props));
73 |
74 | root.appendChild(element);
75 |
76 | expect(root.innerHTML).toContain(`${props.value} `);
77 | });
78 |
79 | it('renders component correctly when from props attribute', async () => {
80 | const props = { value: 'propsValue' };
81 |
82 | define('message-one', () => Message);
83 |
84 | const element = document.createElement('message-one');
85 |
86 | element.setAttribute('props', JSON.stringify(props));
87 |
88 | root.appendChild(element);
89 |
90 | expect(root.innerHTML).toContain(`${props.value} `);
91 | });
92 |
93 | it('renders component correctly when from json script block', async () => {
94 | const props = { value: 'jsonValue' };
95 | const json = ``;
96 |
97 | define('message-two', () => Message);
98 |
99 | const element = document.createElement('message-two');
100 |
101 | element.innerHTML = json;
102 |
103 | root.appendChild(element);
104 |
105 | expect(root.innerHTML).toContain(`${props.value} `);
106 | });
107 |
108 | it('sets contained HTML as children prop when not server rendered', async () => {
109 | const props = { value: 'childMarkup' };
110 | const json = ``;
111 | const html = '
Testing
Click here ';
112 |
113 | define('message-three', () => Message);
114 |
115 | const element = document.createElement('message-three');
116 |
117 | element.innerHTML = json + html;
118 |
119 | root.appendChild(element);
120 |
121 | expect(root.innerHTML).toContain(`${props.value} ${html}`);
122 | });
123 |
124 | it('does not use contained HTML if server rendered', async () => {
125 | const props = { value: 'serverRender' };
126 | const json = ``;
127 | const html = 'Server rendered!
Click here ';
128 |
129 | define('message-four', () => Message);
130 |
131 | const element = document.createElement('message-four');
132 |
133 | element.setAttribute('server', '');
134 | element.innerHTML = json + html;
135 |
136 | root.appendChild(element);
137 |
138 | expect(root.innerHTML).toContain(`${props.value} `);
139 | });
140 |
141 | it('renders component asynchronously when provided', async () => {
142 | const props = { value: 'asyncValue' };
143 | const json = ``;
144 |
145 | define('message-five', () => Promise.resolve(Message));
146 |
147 | const element = document.createElement('message-five');
148 |
149 | element.innerHTML = json;
150 |
151 | root.appendChild(element);
152 |
153 | await flushPromises();
154 |
155 | expect(root.innerHTML).toContain(`${props.value} `);
156 | });
157 |
158 | it('tries to infer the component if not explicitly returned', async () => {
159 | const props = { value: 'inferValue' };
160 | const json = ``;
161 |
162 | define('message-six', () => Promise.resolve({ MessageSix: Message }));
163 |
164 | const element = document.createElement('message-six');
165 |
166 | element.innerHTML = json;
167 |
168 | root.appendChild(element);
169 |
170 | await flushPromises();
171 |
172 | expect(root.innerHTML).toContain(`${props.value} `);
173 | });
174 |
175 | it('merges defined attributes in array with component props', () => {
176 | const customTitle = 'customTitle';
177 | const props = { value: 'attrProps' };
178 | const json = ``;
179 |
180 | define('message-seven', () => Message, { attributes: ['custom-title'] });
181 |
182 | const element = document.createElement('message-seven');
183 |
184 | element.setAttribute('custom-title', customTitle);
185 | element.innerHTML = json;
186 |
187 | root.appendChild(element);
188 |
189 | expect(root.innerHTML).toContain(`${customTitle} ${props.value} `);
190 | });
191 |
192 | it('errors if component cannot be found in function', async () => {
193 | define('message-eight', () => Promise.resolve({ Message }));
194 |
195 | const element = document.createElement('message-eight');
196 |
197 | root.appendChild(element);
198 |
199 | await flushPromises();
200 |
201 | expect(errorSpy).toBeCalled();
202 | expect(element.innerHTML).toEqual('');
203 | });
204 |
205 | it('updates component props when attributes are changed', () => {
206 | const customTitle = 'customTitle';
207 | const updatedProp = 'updated!';
208 | const props = { value: 'attrUpdate' };
209 | const html = 'Click here ';
210 |
211 | define('message-nine', () => Message, { attributes: ['custom-title'] });
212 |
213 | const element = document.createElement('message-nine');
214 |
215 | element.setAttribute('custom-title', customTitle);
216 | element.setAttribute('props', JSON.stringify(props));
217 |
218 | element.innerHTML = html;
219 |
220 | root.appendChild(element);
221 |
222 | expect(root.innerHTML).toContain(`${customTitle} ${props.value} ${html}`);
223 |
224 | element.setAttribute('custom-title', '');
225 | element.setAttribute('props', JSON.stringify({ ...props, value: updatedProp }));
226 |
227 | expect(root.innerHTML).toContain(`${updatedProp} ${html}`);
228 | });
229 |
230 | it('wraps component in an HOC if provided', () => {
231 | const props = { value: 'wrapComponent' };
232 | const json = ``;
233 |
234 | const wrapComponent = (child: ComponentFactory) => (props: any) =>
235 | h('section', {}, h(child, props));
236 |
237 | define('message-ten', () => Message, { wrapComponent });
238 |
239 | const element = document.createElement('message-ten');
240 |
241 | element.innerHTML = json;
242 |
243 | root.appendChild(element);
244 |
245 | expect(root.innerHTML).toContain(``);
246 | });
247 |
248 | it('correctly passes props through formatProps if provided', () => {
249 | const props = { Value: 'formatProps' };
250 | const json = ``;
251 |
252 | const formatProps = (props: any) => {
253 | const keys = Object.keys(props);
254 |
255 | return keys.reduce((result, key) => {
256 | result[key.toLowerCase()] = props[key];
257 |
258 | return result;
259 | }, {});
260 | };
261 |
262 | define('message-eleven', () => Message, { formatProps });
263 |
264 | const element = document.createElement('message-eleven');
265 |
266 | element.innerHTML = json;
267 |
268 | root.appendChild(element);
269 |
270 | expect(root.innerHTML).toContain(`${props.Value} `);
271 | });
272 |
273 | it('correctly segments <* slot="{key}" /> elements into props', () => {
274 | const customTitle = 'customTitle ';
275 | const html = `${customTitle}
`;
276 |
277 | define('message-twelve', () => Message);
278 |
279 | const element = document.createElement('message-twelve');
280 |
281 | element.innerHTML = html;
282 |
283 | root.appendChild(element);
284 |
285 | expect(root.innerHTML).toContain(`${customTitle} `);
286 | });
287 |
288 | it('correctly caches children when moved in the DOM', () => {
289 | const customTitle = 'customTitle ';
290 | const customText = 'Lorem ipsum dolor';
291 | const html = `${customTitle}
${customText}
`;
292 |
293 | define('message-thirteen', () => Message);
294 |
295 | const element = document.createElement('message-thirteen');
296 | const wrapper = document.createElement('main');
297 |
298 | element.innerHTML = html;
299 |
300 | root.appendChild(element);
301 |
302 | element.remove();
303 |
304 | expect(root.innerHTML).toContain('');
305 |
306 | root.appendChild(wrapper);
307 | wrapper.appendChild(element);
308 |
309 | expect(root.innerHTML).toContain(`${customTitle} ${customText}
`);
310 | });
311 |
312 | it('hydrates server rendered markup corretly without re-rendering child elements', () => {
313 | const customTitle = 'customTitle';
314 | const props = { value: 'attrProps' };
315 | const json = ``;
316 | const html = `${customTitle} ${props.value} `;
317 |
318 | define('message-fourteen', () => Message, { attributes: ['custom-title'] });
319 |
320 | const element = document.createElement('message-fourteen');
321 |
322 | element.setAttribute('custom-title', customTitle);
323 | element.setAttribute('server', '');
324 | element.innerHTML = json + html;
325 | const originalH2 = element.querySelector('h2');
326 |
327 | root.appendChild(element);
328 |
329 | expect(element.innerHTML).toEqual(html);
330 | expect(element.querySelector('h2')).toBe(originalH2);
331 | });
332 | });
333 |
334 | describe('when run in the browser (no "Reflect.construct")', () => {
335 | const { document, Reflect } = globalThis;
336 | let root;
337 |
338 | beforeAll(() => {
339 | delete (globalThis as any).Reflect;
340 | });
341 |
342 | beforeEach(() => {
343 | root = document.createElement('div');
344 | document.body.appendChild(root);
345 | });
346 |
347 | afterEach(() => {
348 | document.body.removeChild(root);
349 | });
350 |
351 | afterAll(() => {
352 | globalThis.Reflect = Reflect;
353 | });
354 |
355 | it('renders component correctly', () => {
356 | const customTitle = 'customTitle';
357 | const props = { value: 'attrUpdate' };
358 | const json = ``;
359 | const html = 'Click here ';
360 |
361 | define('message-class', () => Message, { attributes: ['custom-title'] });
362 |
363 | const element = document.createElement('message-class');
364 |
365 | element.setAttribute('custom-title', customTitle);
366 | element.innerHTML = json + html;
367 |
368 | root.appendChild(element);
369 |
370 | expect(root.innerHTML).toContain(`${customTitle} ${props.value} ${html}`);
371 |
372 | element.setAttribute('custom-title', '');
373 |
374 | expect(root.innerHTML).toContain(`${props.value} ${html}`);
375 | });
376 | });
377 |
378 | describe('when run on the server', () => {
379 | const { window } = globalThis;
380 |
381 | beforeAll(() => {
382 | delete (globalThis as any).window;
383 | });
384 |
385 | afterAll(() => {
386 | globalThis.window = window;
387 | });
388 |
389 | it('returns the correct markup', () => {
390 | const props = { value: 'serverValue' };
391 | const component = define('message-one', () => Message);
392 |
393 | const instance = mount(h(component, props) as any);
394 |
395 | expect(instance.find('message-one').length).toEqual(1);
396 | expect(instance.find('message-one').prop('server')).toEqual(true);
397 | expect(instance.find('em').text()).toEqual(props.value);
398 | });
399 |
400 | it('throws an error when used with a promise', () => {
401 | expect(() => define('message-two', () => Promise.resolve(Message))).toThrow();
402 | });
403 |
404 | it('includes a json script block with props', () => {
405 | const props = { value: 'serverValue' };
406 | const component = define('message-three', () => Message);
407 |
408 | const instance = mount(h(component, props) as any);
409 |
410 | expect(instance.find('script').text()).toEqual(JSON.stringify(props));
411 | });
412 | });
413 | });
414 |
--------------------------------------------------------------------------------
/packages/preactement/tests/parse.spec.ts:
--------------------------------------------------------------------------------
1 | import { h } from 'preact';
2 | import { mount } from 'enzyme';
3 | import { parseHtml } from '../src/parse';
4 |
5 | /* -----------------------------------
6 | *
7 | * Variables
8 | *
9 | * -------------------------------- */
10 |
11 | const testHeading = 'testHeading';
12 | const testWhitespace = ' ';
13 | const testHtml = `${testHeading} `;
14 | const testScript = ``;
15 |
16 | /* -----------------------------------
17 | *
18 | * Parse
19 | *
20 | * -------------------------------- */
21 |
22 | describe('parse', () => {
23 | describe('parseHtml()', () => {
24 | it('should correctly handle misformed html', () => {
25 | const testText = 'testText';
26 | const result = parseHtml.call({ innerHTML: `${testText}` });
27 | const instance = mount(h(result, {}) as any);
28 |
29 | expect(instance.html()).toEqual(`${testText} `);
30 | });
31 |
32 | it('handles text values witin custom element', () => {
33 | const result = parseHtml.call({ innerHTML: testHeading });
34 | const instance = mount(h(result, {}) as any);
35 |
36 | expect(instance.text()).toEqual(testHeading);
37 | });
38 |
39 | it('retains whitespace within custom element', () => {
40 | const result = parseHtml.call({ innerHTML: testWhitespace });
41 | const instance = mount(h(result, {}) as any);
42 |
43 | expect(instance.text()).toEqual(testWhitespace);
44 | expect(instance.html()).toEqual(testWhitespace);
45 | });
46 |
47 | it('removes script blocks for security', () => {
48 | const result = parseHtml.call({ innerHTML: testScript });
49 | const instance = mount(h(result, {}) as any);
50 |
51 | expect(instance.text()).toEqual('');
52 | });
53 |
54 | it('correctly converts an HTML string into a VDom tree', () => {
55 | const result = parseHtml.call({ innerHTML: testHtml });
56 | const instance = mount(h(result, {}) as any);
57 |
58 | expect(instance.find('h1').text()).toEqual(testHeading);
59 | });
60 |
61 | describe('slots', () => {
62 | const testKey = 'testSlot';
63 |
64 | it('should remove <* slot="{key}"> and apply to props', () => {
65 | const slots = {};
66 | const slotValue = 'slotValue';
67 |
68 | const slotHtml = `${slotValue} `;
69 | const headingHtml = `${testHeading} `;
70 | const testHtml = `${headingHtml}${slotHtml} `;
71 |
72 | const result = parseHtml.call({ innerHTML: testHtml, __slots: slots });
73 | const instance = mount(h(result, {}) as any);
74 |
75 | expect(instance.html()).toEqual(``);
76 | expect(slots).toEqual({ [testKey]: slotValue });
77 | });
78 | });
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/packages/preactement/tests/setup.ts:
--------------------------------------------------------------------------------
1 | import { configure } from 'enzyme';
2 | import Adapter from 'enzyme-adapter-preact-pure';
3 |
4 | /* -----------------------------------
5 | *
6 | * Setup
7 | *
8 | * -------------------------------- */
9 |
10 | configure({ adapter: new Adapter() });
11 |
--------------------------------------------------------------------------------
/packages/preactement/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "jsx": "react",
6 | "jsxFactory": "h"
7 | },
8 | "include": ["./src"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/preactement/webpack.config.ts:
--------------------------------------------------------------------------------
1 | import { Configuration, DefinePlugin } from 'webpack';
2 | import nodeExternals from 'webpack-node-externals';
3 | import * as path from 'path';
4 |
5 | /* -----------------------------------
6 | *
7 | * Output
8 | *
9 | * -------------------------------- */
10 |
11 | const outputFiles = [
12 | { target: 'es5', filename: '[name].es5.js' },
13 | { target: 'es2016', filename: '[name].js' },
14 | ];
15 |
16 | /* -----------------------------------
17 | *
18 | * Default
19 | *
20 | * -------------------------------- */
21 |
22 | const defaultConfig = {
23 | entry: {
24 | define: path.join(__dirname, './src/define.ts'),
25 | },
26 | externals: [
27 | nodeExternals({
28 | allowlist: ['@component-elements/shared'],
29 | modulesDir: path.resolve(__dirname, '../../node_modules'),
30 | }),
31 | ],
32 | context: path.join(__dirname, './src'),
33 | output: {
34 | path: path.join(__dirname, './dist'),
35 | filename: '[name].js',
36 | libraryTarget: 'umd',
37 | globalObject: 'this',
38 | chunkFormat: 'commonjs',
39 | },
40 | resolve: {
41 | extensions: ['.js', '.ts', '.tsx', 'json'],
42 | },
43 | node: {
44 | __filename: true,
45 | __dirname: true,
46 | },
47 | stats: {
48 | colors: true,
49 | timings: true,
50 | },
51 | };
52 |
53 | /* -----------------------------------
54 | *
55 | * Config
56 | *
57 | * -------------------------------- */
58 |
59 | const config = ({ mode }): Configuration[] =>
60 | outputFiles.map(({ target, filename, ...config }) => ({
61 | ...defaultConfig,
62 | mode,
63 | target,
64 | devtool: mode === 'development' ? 'eval-source-map' : void 0,
65 | cache: mode === 'development',
66 | output: {
67 | ...defaultConfig.output,
68 | filename,
69 | },
70 | module: {
71 | rules: [
72 | {
73 | test: /\.ts$/,
74 | use: [
75 | {
76 | loader: 'ts-loader',
77 | options: {
78 | compilerOptions: {
79 | target,
80 | },
81 | },
82 | },
83 | ],
84 | },
85 | {
86 | test: /\.m?js$/,
87 | use: {
88 | loader: 'babel-loader',
89 | options: {
90 | ...(target === 'es5' && { presets: ['@babel/preset-env'] }),
91 | },
92 | },
93 | },
94 | ],
95 | },
96 | performance: {
97 | hints: mode === 'development' ? 'warning' : void 0,
98 | },
99 | plugins: [
100 | new DefinePlugin({
101 | __DEV__: mode === 'development',
102 | }),
103 | ],
104 | ...config,
105 | }));
106 |
107 | /* -----------------------------------
108 | *
109 | * Export
110 | *
111 | * -------------------------------- */
112 |
113 | module.exports = config;
114 |
--------------------------------------------------------------------------------
/packages/preactement/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@types/cheerio@*":
6 | "integrity" "sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw=="
7 | "resolved" "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.31.tgz"
8 | "version" "0.22.31"
9 | dependencies:
10 | "@types/node" "*"
11 |
12 | "@types/enzyme@^3.10.11":
13 | "integrity" "sha512-LEtC7zXsQlbGXWGcnnmOI7rTyP+i1QzQv4Va91RKXDEukLDaNyxu0rXlfMiGEhJwfgTPCTb0R+Pnlj//oM9e/w=="
14 | "resolved" "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.11.tgz"
15 | "version" "3.10.11"
16 | dependencies:
17 | "@types/cheerio" "*"
18 | "@types/react" "*"
19 |
20 | "@types/node@*":
21 | "integrity" "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA=="
22 | "resolved" "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz"
23 | "version" "17.0.18"
24 |
25 | "@types/prop-types@*":
26 | "integrity" "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
27 | "resolved" "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz"
28 | "version" "15.7.4"
29 |
30 | "@types/react@*":
31 | "integrity" "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug=="
32 | "resolved" "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz"
33 | "version" "17.0.39"
34 | dependencies:
35 | "@types/prop-types" "*"
36 | "@types/scheduler" "*"
37 | "csstype" "^3.0.2"
38 |
39 | "@types/scheduler@*":
40 | "integrity" "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
41 | "resolved" "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz"
42 | "version" "0.16.2"
43 |
44 | "array.prototype.filter@^1.0.0":
45 | "integrity" "sha512-Dk3Ty7N42Odk7PjU/Ci3zT4pLj20YvuVnneG/58ICM6bt4Ij5kZaJTVQ9TSaWaIECX2sFyz4KItkVZqHNnciqw=="
46 | "resolved" "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.1.tgz"
47 | "version" "1.0.1"
48 | dependencies:
49 | "call-bind" "^1.0.2"
50 | "define-properties" "^1.1.3"
51 | "es-abstract" "^1.19.0"
52 | "es-array-method-boxes-properly" "^1.0.0"
53 | "is-string" "^1.0.7"
54 |
55 | "array.prototype.flat@^1.2.3":
56 | "integrity" "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg=="
57 | "resolved" "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz"
58 | "version" "1.2.5"
59 | dependencies:
60 | "call-bind" "^1.0.2"
61 | "define-properties" "^1.1.3"
62 | "es-abstract" "^1.19.0"
63 |
64 | "array.prototype.flatmap@^1.2.1":
65 | "integrity" "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA=="
66 | "resolved" "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz"
67 | "version" "1.2.5"
68 | dependencies:
69 | "call-bind" "^1.0.0"
70 | "define-properties" "^1.1.3"
71 | "es-abstract" "^1.19.0"
72 |
73 | "boolbase@^1.0.0":
74 | "integrity" "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
75 | "resolved" "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz"
76 | "version" "1.0.0"
77 |
78 | "call-bind@^1.0.0", "call-bind@^1.0.2":
79 | "integrity" "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA=="
80 | "resolved" "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz"
81 | "version" "1.0.2"
82 | dependencies:
83 | "function-bind" "^1.1.1"
84 | "get-intrinsic" "^1.0.2"
85 |
86 | "cheerio-select@^1.5.0":
87 | "integrity" "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg=="
88 | "resolved" "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz"
89 | "version" "1.5.0"
90 | dependencies:
91 | "css-select" "^4.1.3"
92 | "css-what" "^5.0.1"
93 | "domelementtype" "^2.2.0"
94 | "domhandler" "^4.2.0"
95 | "domutils" "^2.7.0"
96 |
97 | "cheerio@^1.0.0-rc.3":
98 | "integrity" "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw=="
99 | "resolved" "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz"
100 | "version" "1.0.0-rc.10"
101 | dependencies:
102 | "cheerio-select" "^1.5.0"
103 | "dom-serializer" "^1.3.2"
104 | "domhandler" "^4.2.0"
105 | "htmlparser2" "^6.1.0"
106 | "parse5" "^6.0.1"
107 | "parse5-htmlparser2-tree-adapter" "^6.0.1"
108 | "tslib" "^2.2.0"
109 |
110 | "commander@^2.19.0":
111 | "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
112 | "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz"
113 | "version" "2.20.3"
114 |
115 | "css-select@^4.1.3":
116 | "integrity" "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ=="
117 | "resolved" "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz"
118 | "version" "4.2.1"
119 | dependencies:
120 | "boolbase" "^1.0.0"
121 | "css-what" "^5.1.0"
122 | "domhandler" "^4.3.0"
123 | "domutils" "^2.8.0"
124 | "nth-check" "^2.0.1"
125 |
126 | "css-what@^5.0.1", "css-what@^5.1.0":
127 | "integrity" "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw=="
128 | "resolved" "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz"
129 | "version" "5.1.0"
130 |
131 | "csstype@^3.0.2":
132 | "integrity" "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
133 | "resolved" "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz"
134 | "version" "3.0.10"
135 |
136 | "define-properties@^1.1.3":
137 | "integrity" "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ=="
138 | "resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz"
139 | "version" "1.1.3"
140 | dependencies:
141 | "object-keys" "^1.0.12"
142 |
143 | "discontinuous-range@1.0.0":
144 | "integrity" "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo="
145 | "resolved" "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz"
146 | "version" "1.0.0"
147 |
148 | "dom-serializer@^1.0.1", "dom-serializer@^1.3.2":
149 | "integrity" "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig=="
150 | "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz"
151 | "version" "1.3.2"
152 | dependencies:
153 | "domelementtype" "^2.0.1"
154 | "domhandler" "^4.2.0"
155 | "entities" "^2.0.0"
156 |
157 | "domelementtype@^2.0.1", "domelementtype@^2.2.0":
158 | "integrity" "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
159 | "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz"
160 | "version" "2.2.0"
161 |
162 | "domhandler@^4.0.0", "domhandler@^4.2.0", "domhandler@^4.3.0":
163 | "integrity" "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g=="
164 | "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz"
165 | "version" "4.3.0"
166 | dependencies:
167 | "domelementtype" "^2.2.0"
168 |
169 | "domutils@^2.5.2", "domutils@^2.7.0", "domutils@^2.8.0":
170 | "integrity" "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="
171 | "resolved" "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz"
172 | "version" "2.8.0"
173 | dependencies:
174 | "dom-serializer" "^1.0.1"
175 | "domelementtype" "^2.2.0"
176 | "domhandler" "^4.2.0"
177 |
178 | "entities@^2.0.0":
179 | "integrity" "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
180 | "resolved" "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz"
181 | "version" "2.2.0"
182 |
183 | "enzyme-adapter-preact-pure@^3.3.0":
184 | "integrity" "sha512-+FNEZBEXwuDDsA8YOvC9p6gcvxQG5V6QnXKkUVJte/GKMWadOXDR+uw0w+QGwxreA8oMOlK/1+O8F7PzealpKA=="
185 | "resolved" "https://registry.npmjs.org/enzyme-adapter-preact-pure/-/enzyme-adapter-preact-pure-3.3.0.tgz"
186 | "version" "3.3.0"
187 | dependencies:
188 | "array.prototype.flatmap" "^1.2.1"
189 |
190 | "enzyme-shallow-equal@^1.0.1":
191 | "integrity" "sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q=="
192 | "resolved" "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz"
193 | "version" "1.0.4"
194 | dependencies:
195 | "has" "^1.0.3"
196 | "object-is" "^1.1.2"
197 |
198 | "enzyme@^3.11.0", "enzyme@^3.8.0":
199 | "integrity" "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw=="
200 | "resolved" "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz"
201 | "version" "3.11.0"
202 | dependencies:
203 | "array.prototype.flat" "^1.2.3"
204 | "cheerio" "^1.0.0-rc.3"
205 | "enzyme-shallow-equal" "^1.0.1"
206 | "function.prototype.name" "^1.1.2"
207 | "has" "^1.0.3"
208 | "html-element-map" "^1.2.0"
209 | "is-boolean-object" "^1.0.1"
210 | "is-callable" "^1.1.5"
211 | "is-number-object" "^1.0.4"
212 | "is-regex" "^1.0.5"
213 | "is-string" "^1.0.5"
214 | "is-subset" "^0.1.1"
215 | "lodash.escape" "^4.0.1"
216 | "lodash.isequal" "^4.5.0"
217 | "object-inspect" "^1.7.0"
218 | "object-is" "^1.0.2"
219 | "object.assign" "^4.1.0"
220 | "object.entries" "^1.1.1"
221 | "object.values" "^1.1.1"
222 | "raf" "^3.4.1"
223 | "rst-selector-parser" "^2.2.3"
224 | "string.prototype.trim" "^1.2.1"
225 |
226 | "es-abstract@^1.19.0", "es-abstract@^1.19.1":
227 | "integrity" "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w=="
228 | "resolved" "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz"
229 | "version" "1.19.1"
230 | dependencies:
231 | "call-bind" "^1.0.2"
232 | "es-to-primitive" "^1.2.1"
233 | "function-bind" "^1.1.1"
234 | "get-intrinsic" "^1.1.1"
235 | "get-symbol-description" "^1.0.0"
236 | "has" "^1.0.3"
237 | "has-symbols" "^1.0.2"
238 | "internal-slot" "^1.0.3"
239 | "is-callable" "^1.2.4"
240 | "is-negative-zero" "^2.0.1"
241 | "is-regex" "^1.1.4"
242 | "is-shared-array-buffer" "^1.0.1"
243 | "is-string" "^1.0.7"
244 | "is-weakref" "^1.0.1"
245 | "object-inspect" "^1.11.0"
246 | "object-keys" "^1.1.1"
247 | "object.assign" "^4.1.2"
248 | "string.prototype.trimend" "^1.0.4"
249 | "string.prototype.trimstart" "^1.0.4"
250 | "unbox-primitive" "^1.0.1"
251 |
252 | "es-array-method-boxes-properly@^1.0.0":
253 | "integrity" "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA=="
254 | "resolved" "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz"
255 | "version" "1.0.0"
256 |
257 | "es-to-primitive@^1.2.1":
258 | "integrity" "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA=="
259 | "resolved" "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz"
260 | "version" "1.2.1"
261 | dependencies:
262 | "is-callable" "^1.1.4"
263 | "is-date-object" "^1.0.1"
264 | "is-symbol" "^1.0.2"
265 |
266 | "function-bind@^1.1.1":
267 | "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
268 | "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
269 | "version" "1.1.1"
270 |
271 | "function.prototype.name@^1.1.2":
272 | "integrity" "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA=="
273 | "resolved" "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz"
274 | "version" "1.1.5"
275 | dependencies:
276 | "call-bind" "^1.0.2"
277 | "define-properties" "^1.1.3"
278 | "es-abstract" "^1.19.0"
279 | "functions-have-names" "^1.2.2"
280 |
281 | "functions-have-names@^1.2.2":
282 | "integrity" "sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA=="
283 | "resolved" "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.2.tgz"
284 | "version" "1.2.2"
285 |
286 | "get-intrinsic@^1.0.2", "get-intrinsic@^1.1.0", "get-intrinsic@^1.1.1":
287 | "integrity" "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q=="
288 | "resolved" "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz"
289 | "version" "1.1.1"
290 | dependencies:
291 | "function-bind" "^1.1.1"
292 | "has" "^1.0.3"
293 | "has-symbols" "^1.0.1"
294 |
295 | "get-symbol-description@^1.0.0":
296 | "integrity" "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw=="
297 | "resolved" "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz"
298 | "version" "1.0.0"
299 | dependencies:
300 | "call-bind" "^1.0.2"
301 | "get-intrinsic" "^1.1.1"
302 |
303 | "has-bigints@^1.0.1":
304 | "integrity" "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="
305 | "resolved" "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz"
306 | "version" "1.0.1"
307 |
308 | "has-symbols@^1.0.1", "has-symbols@^1.0.2":
309 | "integrity" "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
310 | "resolved" "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz"
311 | "version" "1.0.2"
312 |
313 | "has-tostringtag@^1.0.0":
314 | "integrity" "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ=="
315 | "resolved" "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz"
316 | "version" "1.0.0"
317 | dependencies:
318 | "has-symbols" "^1.0.2"
319 |
320 | "has@^1.0.3":
321 | "integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw=="
322 | "resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
323 | "version" "1.0.3"
324 | dependencies:
325 | "function-bind" "^1.1.1"
326 |
327 | "html-element-map@^1.2.0":
328 | "integrity" "sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg=="
329 | "resolved" "https://registry.npmjs.org/html-element-map/-/html-element-map-1.3.1.tgz"
330 | "version" "1.3.1"
331 | dependencies:
332 | "array.prototype.filter" "^1.0.0"
333 | "call-bind" "^1.0.2"
334 |
335 | "htmlparser2@^6.1.0":
336 | "integrity" "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A=="
337 | "resolved" "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz"
338 | "version" "6.1.0"
339 | dependencies:
340 | "domelementtype" "^2.0.1"
341 | "domhandler" "^4.0.0"
342 | "domutils" "^2.5.2"
343 | "entities" "^2.0.0"
344 |
345 | "internal-slot@^1.0.3":
346 | "integrity" "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA=="
347 | "resolved" "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz"
348 | "version" "1.0.3"
349 | dependencies:
350 | "get-intrinsic" "^1.1.0"
351 | "has" "^1.0.3"
352 | "side-channel" "^1.0.4"
353 |
354 | "is-bigint@^1.0.1":
355 | "integrity" "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg=="
356 | "resolved" "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz"
357 | "version" "1.0.4"
358 | dependencies:
359 | "has-bigints" "^1.0.1"
360 |
361 | "is-boolean-object@^1.0.1", "is-boolean-object@^1.1.0":
362 | "integrity" "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA=="
363 | "resolved" "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz"
364 | "version" "1.1.2"
365 | dependencies:
366 | "call-bind" "^1.0.2"
367 | "has-tostringtag" "^1.0.0"
368 |
369 | "is-callable@^1.1.4", "is-callable@^1.1.5", "is-callable@^1.2.4":
370 | "integrity" "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="
371 | "resolved" "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz"
372 | "version" "1.2.4"
373 |
374 | "is-date-object@^1.0.1":
375 | "integrity" "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ=="
376 | "resolved" "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz"
377 | "version" "1.0.5"
378 | dependencies:
379 | "has-tostringtag" "^1.0.0"
380 |
381 | "is-negative-zero@^2.0.1":
382 | "integrity" "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA=="
383 | "resolved" "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz"
384 | "version" "2.0.2"
385 |
386 | "is-number-object@^1.0.4":
387 | "integrity" "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g=="
388 | "resolved" "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz"
389 | "version" "1.0.6"
390 | dependencies:
391 | "has-tostringtag" "^1.0.0"
392 |
393 | "is-regex@^1.0.5", "is-regex@^1.1.4":
394 | "integrity" "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg=="
395 | "resolved" "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz"
396 | "version" "1.1.4"
397 | dependencies:
398 | "call-bind" "^1.0.2"
399 | "has-tostringtag" "^1.0.0"
400 |
401 | "is-shared-array-buffer@^1.0.1":
402 | "integrity" "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA=="
403 | "resolved" "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz"
404 | "version" "1.0.1"
405 |
406 | "is-string@^1.0.5", "is-string@^1.0.7":
407 | "integrity" "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg=="
408 | "resolved" "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz"
409 | "version" "1.0.7"
410 | dependencies:
411 | "has-tostringtag" "^1.0.0"
412 |
413 | "is-subset@^0.1.1":
414 | "integrity" "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY="
415 | "resolved" "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz"
416 | "version" "0.1.1"
417 |
418 | "is-symbol@^1.0.2", "is-symbol@^1.0.3":
419 | "integrity" "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg=="
420 | "resolved" "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz"
421 | "version" "1.0.4"
422 | dependencies:
423 | "has-symbols" "^1.0.2"
424 |
425 | "is-weakref@^1.0.1":
426 | "integrity" "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ=="
427 | "resolved" "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz"
428 | "version" "1.0.2"
429 | dependencies:
430 | "call-bind" "^1.0.2"
431 |
432 | "lodash.escape@^4.0.1":
433 | "integrity" "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg="
434 | "resolved" "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz"
435 | "version" "4.0.1"
436 |
437 | "lodash.flattendeep@^4.4.0":
438 | "integrity" "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI="
439 | "resolved" "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz"
440 | "version" "4.4.0"
441 |
442 | "lodash.isequal@^4.5.0":
443 | "integrity" "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
444 | "resolved" "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz"
445 | "version" "4.5.0"
446 |
447 | "moo@^0.5.0":
448 | "integrity" "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w=="
449 | "resolved" "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz"
450 | "version" "0.5.1"
451 |
452 | "nearley@^2.7.10":
453 | "integrity" "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ=="
454 | "resolved" "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz"
455 | "version" "2.20.1"
456 | dependencies:
457 | "commander" "^2.19.0"
458 | "moo" "^0.5.0"
459 | "railroad-diagrams" "^1.0.0"
460 | "randexp" "0.4.6"
461 |
462 | "nth-check@^2.0.1":
463 | "integrity" "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w=="
464 | "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz"
465 | "version" "2.0.1"
466 | dependencies:
467 | "boolbase" "^1.0.0"
468 |
469 | "object-inspect@^1.11.0", "object-inspect@^1.7.0", "object-inspect@^1.9.0":
470 | "integrity" "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
471 | "resolved" "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz"
472 | "version" "1.12.0"
473 |
474 | "object-is@^1.0.2", "object-is@^1.1.2":
475 | "integrity" "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw=="
476 | "resolved" "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz"
477 | "version" "1.1.5"
478 | dependencies:
479 | "call-bind" "^1.0.2"
480 | "define-properties" "^1.1.3"
481 |
482 | "object-keys@^1.0.12", "object-keys@^1.1.1":
483 | "integrity" "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
484 | "resolved" "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
485 | "version" "1.1.1"
486 |
487 | "object.assign@^4.1.0", "object.assign@^4.1.2":
488 | "integrity" "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ=="
489 | "resolved" "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz"
490 | "version" "4.1.2"
491 | dependencies:
492 | "call-bind" "^1.0.0"
493 | "define-properties" "^1.1.3"
494 | "has-symbols" "^1.0.1"
495 | "object-keys" "^1.1.1"
496 |
497 | "object.entries@^1.1.1":
498 | "integrity" "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g=="
499 | "resolved" "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz"
500 | "version" "1.1.5"
501 | dependencies:
502 | "call-bind" "^1.0.2"
503 | "define-properties" "^1.1.3"
504 | "es-abstract" "^1.19.1"
505 |
506 | "object.values@^1.1.1":
507 | "integrity" "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg=="
508 | "resolved" "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz"
509 | "version" "1.1.5"
510 | dependencies:
511 | "call-bind" "^1.0.2"
512 | "define-properties" "^1.1.3"
513 | "es-abstract" "^1.19.1"
514 |
515 | "parse5-htmlparser2-tree-adapter@^6.0.1":
516 | "integrity" "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA=="
517 | "resolved" "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz"
518 | "version" "6.0.1"
519 | dependencies:
520 | "parse5" "^6.0.1"
521 |
522 | "parse5@^6.0.1":
523 | "integrity" "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
524 | "resolved" "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz"
525 | "version" "6.0.1"
526 |
527 | "performance-now@^2.1.0":
528 | "integrity" "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
529 | "resolved" "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz"
530 | "version" "2.1.0"
531 |
532 | "preact@^10.0.0", "preact@^10.6.6":
533 | "integrity" "sha512-dgxpTFV2vs4vizwKohYKkk7g7rmp1wOOcfd4Tz3IB3Wi+ivZzsn/SpeKJhRENSE+n8sUfsAl4S3HiCVT923ABw=="
534 | "resolved" "https://registry.npmjs.org/preact/-/preact-10.6.6.tgz"
535 | "version" "10.6.6"
536 |
537 | "raf@^3.4.1":
538 | "integrity" "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA=="
539 | "resolved" "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz"
540 | "version" "3.4.1"
541 | dependencies:
542 | "performance-now" "^2.1.0"
543 |
544 | "railroad-diagrams@^1.0.0":
545 | "integrity" "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234="
546 | "resolved" "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz"
547 | "version" "1.0.0"
548 |
549 | "randexp@0.4.6":
550 | "integrity" "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ=="
551 | "resolved" "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz"
552 | "version" "0.4.6"
553 | dependencies:
554 | "discontinuous-range" "1.0.0"
555 | "ret" "~0.1.10"
556 |
557 | "ret@~0.1.10":
558 | "integrity" "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
559 | "resolved" "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz"
560 | "version" "0.1.15"
561 |
562 | "rst-selector-parser@^2.2.3":
563 | "integrity" "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE="
564 | "resolved" "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz"
565 | "version" "2.2.3"
566 | dependencies:
567 | "lodash.flattendeep" "^4.4.0"
568 | "nearley" "^2.7.10"
569 |
570 | "side-channel@^1.0.4":
571 | "integrity" "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw=="
572 | "resolved" "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz"
573 | "version" "1.0.4"
574 | dependencies:
575 | "call-bind" "^1.0.0"
576 | "get-intrinsic" "^1.0.2"
577 | "object-inspect" "^1.9.0"
578 |
579 | "string.prototype.trim@^1.2.1":
580 | "integrity" "sha512-Lnh17webJVsD6ECeovpVN17RlAKjmz4rF9S+8Y45CkMc/ufVpTkU3vZIyIC7sllQ1FCvObZnnCdNs/HXTUOTlg=="
581 | "resolved" "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz"
582 | "version" "1.2.5"
583 | dependencies:
584 | "call-bind" "^1.0.2"
585 | "define-properties" "^1.1.3"
586 | "es-abstract" "^1.19.1"
587 |
588 | "string.prototype.trimend@^1.0.4":
589 | "integrity" "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A=="
590 | "resolved" "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz"
591 | "version" "1.0.4"
592 | dependencies:
593 | "call-bind" "^1.0.2"
594 | "define-properties" "^1.1.3"
595 |
596 | "string.prototype.trimstart@^1.0.4":
597 | "integrity" "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw=="
598 | "resolved" "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz"
599 | "version" "1.0.4"
600 | dependencies:
601 | "call-bind" "^1.0.2"
602 | "define-properties" "^1.1.3"
603 |
604 | "tslib@^2.2.0":
605 | "integrity" "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
606 | "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz"
607 | "version" "2.3.1"
608 |
609 | "unbox-primitive@^1.0.1":
610 | "integrity" "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw=="
611 | "resolved" "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz"
612 | "version" "1.0.1"
613 | dependencies:
614 | "function-bind" "^1.1.1"
615 | "has-bigints" "^1.0.1"
616 | "has-symbols" "^1.0.2"
617 | "which-boxed-primitive" "^1.0.2"
618 |
619 | "which-boxed-primitive@^1.0.2":
620 | "integrity" "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg=="
621 | "resolved" "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz"
622 | "version" "1.0.2"
623 | dependencies:
624 | "is-bigint" "^1.0.1"
625 | "is-boolean-object" "^1.1.0"
626 | "is-number-object" "^1.0.4"
627 | "is-string" "^1.0.5"
628 | "is-symbol" "^1.0.3"
629 |
--------------------------------------------------------------------------------
/packages/reactement/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmjs.org
2 |
--------------------------------------------------------------------------------
/packages/reactement/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 James Hill
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.
22 |
--------------------------------------------------------------------------------
/packages/reactement/README.md:
--------------------------------------------------------------------------------
1 | # reactement
2 |
3 | Sometimes it's useful to let the DOM render our components when needed. Custom Elements are great at this. They provide various methods that can inform you when an element is "connected" or "disconnected" from the DOM.
4 |
5 | This package (only **2KB** GZipped) provides the ability to use an HTML custom element as the root for your components. In addition, it allows the use of async code resolution if your custom element isn't immediately used, which is a great strategy for reducing code weight. The exported function can also be used for hydration from SSR in Node.
6 |
7 | It's also a great way for you to integrate React into other server side frameworks that might render your HTML.
8 |
9 | > This package supports React. If you're using Preact, go to [preactement](https://github.com/jahilldev/component-elements/tree/main/packages/preactement#readme) for more info.
10 |
11 | # Getting Started
12 |
13 | Install with Yarn:
14 |
15 | ```bash
16 | $ yarn add reactement
17 | ```
18 |
19 | Install with NPM:
20 |
21 | ```bash
22 | $ npm i reactement
23 | ```
24 |
25 | # Using define()
26 |
27 | `reactement` exports one function, `define()`. This allows us to register a custom element via a provided key, and provide the component we'd like to render within. It can also generate a custom element with props ready for hydration if run on the server.
28 |
29 | The first argument **must be a valid custom element string**, e.g hyphenated. If you do not provide this, a prefix of `component-` will be applied to your element name.
30 |
31 | ## In the browser
32 |
33 | In order to register and render a component, you'll need to call `define()` with your chosen component, e.g:
34 |
35 | ```tsx
36 | import { define } from 'reactement';
37 | import { HeroBanner } from './heroBanner';
38 |
39 | /*[...]*/
40 |
41 | define('hero-banner', () => HeroBanner);
42 | ```
43 |
44 | This registers `` as a custom element. When that element exists on the page, `reactement` will render our component.
45 |
46 | If the custom element isn't present immediately, e.g it's created dynamically at some point in the future, we can provide an async function that explicitly resolves your component:
47 |
48 | ```tsx
49 | define('hero-banner', () => Promise.resolve(HeroBanner));
50 | ```
51 |
52 | This allows us to reduce the overall code in our bundle, and load the required component on demand when needed.
53 |
54 | You can either resolve the component from your async function, as seen above, _or_ `reactement` will try to infer the export key based on the provided tag name. For example:
55 |
56 | ```tsx
57 | import { define } from 'reactement';
58 |
59 | /*[...]*/
60 |
61 | define('hero-banner', () => import('./heroBanner'));
62 | ```
63 |
64 | As the `heroBanner.ts` file is exporting the component as a key, e.g `export { HeroBanner };`, and this matches the tag name in kebab-case, e.g `hero-banner`, the component will be correctly rendered.
65 |
66 | ## On the server (SSR)
67 |
68 | You can also use `define()` to generate a custom element container if you're rendering your page in Node. When wrapping your component, e.g:
69 |
70 | ```ts
71 | define('hero-banner', () => HeroBanner);
72 | ```
73 |
74 | A functional component is returned that you can include elsewhere in your app. For example:
75 |
76 | ```tsx
77 | import { define } from 'reactement';
78 |
79 | /*[...]*/
80 |
81 | const Component = define('hero-banner', () => HeroBanner);
82 |
83 | /*[...]*/
84 |
85 | function HomePage() {
86 | return (
87 |
88 |
89 |
90 | );
91 | }
92 | ```
93 |
94 | ## Properties
95 |
96 | If you're not running `reactement` on the server, you have several ways of defining props for your component.
97 |
98 | #### 1. Nested block of JSON:
99 |
100 | ```html
101 |
102 |
105 |
106 | ```
107 |
108 | #### 2. A `props` attribute (this must be an encoded JSON string)
109 |
110 | ```html
111 |
112 | ```
113 |
114 | #### 3. Custom attributes
115 |
116 | ```html
117 |
118 | ```
119 |
120 | You'll need to define your custom attributes up front when using `define()`, e.g:
121 |
122 | ```ts
123 | define('hero-banner', () => HeroBanner, { attributes: ['title-text'] });
124 | ```
125 |
126 | These will then be merged into your components props in camelCase, so `title-text` will become `titleText`.
127 |
128 | ## HTML
129 |
130 | You can also provide nested HTML to your components `children` property. For example:
131 |
132 | ```html
133 |
134 | Banner Title
135 |
136 | ```
137 |
138 | This will correctly convert the `` into virtual DOM nodes for use in your component, e.g:
139 |
140 | ```tsx
141 | /*[...]*/
142 |
143 | function HeroBanner({ children }) {
144 | return ;
145 | }
146 | ```
147 |
148 | ### Important
149 |
150 | Any HTML provided to the custom element **must be valid**; As we're using the DOM's native parser which is quite lax, any html passed that is not properly sanitised or structured might result in unusual bugs. For example:
151 |
152 | This will result in a React error:
153 |
154 | ```jsx
155 | Hello
162 |
Hello
163 | ```
164 |
165 | ### Slots
166 |
167 | `reactement` now supports the use of `<* slot="{key}" />` elements, to assign string values or full blocks of HTML to your component props. This is useful if your server defines layout rules that are outside of the scope of your component. For example, given the custom element below:
168 |
169 | ```html
170 |
171 | Please Login
172 |
173 |
You have successfully logged in, congrats!
174 |
Continue
175 |
176 |
177 | ```
178 |
179 | All elements that have a `slot` attribute will be segmented into your components props, using the provided `slot="{value}"` as the key, e.g:
180 |
181 | ```tsx
182 | function LoginForm({ successMessage }) {
183 | const [isLoggedIn, setLoggedIn] = useState(false);
184 |
185 | return (
186 |
187 | {isLoggedIn && successMessage}
188 |
189 |
190 | );
191 | }
192 | ```
193 |
194 | It's important to note that **slot keys will be normalised into camelCase**, for example: `slot="my-slot"` will be accessed via `mySlot` in your component's props. It's recommended to use camelCase for slot keys, but this isn't always possible. `reactement` will do it's best to handle all common casing conventions, e.g kebab-case, snake_case and PascalCase. Slot values can be either primitive strings, or full HTML structures, as seen in the example above.
195 |
196 | ## Options
197 |
198 | `define` has a third argument, "options". For example:
199 |
200 | ```javascript
201 | define('hero-banner', () => HeroBanner, {
202 | /*[options]*/
203 | });
204 | ```
205 |
206 | ### attributes
207 |
208 | If you require custom attributes to be passed down to your component, you'll need to specify them in this array. For example:
209 |
210 | ```javascript
211 | define('hero-banner', () => HeroBanner, { attributes: ['banner-title'] });
212 | ```
213 |
214 | And the custom element will look like the following:
215 |
216 | ```html
217 |
218 | ```
219 |
220 | ### formatProps
221 |
222 | This allows you to provide a function to process or format your props prior to the component being rendered. One use case is changing property casings. If the data provided by your server uses Pascal, but your components make use of the standard camelCase, this function will allow you to consolidate them.
223 |
224 | ### wrapComponent
225 |
226 | If you need to wrap your component prior to render with a higher order function, you can provide it here. For example, if you asynchronously resolve your component, but also make use of Redux, you'll need to provide a `wrapComponent` function to apply the Provider HOC etc. It can also be useful for themeing, or other use cases.
227 |
228 | ## Useful things
229 |
230 | By default, all components will be provided with a `parent` prop. This is a reference to the root element that the component has been rendered within. This can be useful when working with Web Components, or you wish to apply changes to the custom element. This will **only be defined when run on the client**.
231 |
232 | ## ES5 Support
233 |
234 | To support ES5 or older browsers, like IE11, you'll need to either transpile `reactement`, or import the ES5 version via `reactement/es5`, while also installing the official Web Component [Custom Element polyfill](https://www.npmjs.com/package/@webcomponents/custom-elements). Once installed, you'll need to import the following at the very top of your entry files:
235 |
236 | ```javascript
237 | import '@webcomponents/custom-elements';
238 | import '@webcomponents/custom-elements/src/native-shim';
239 | ```
240 |
241 | # Acknowledgement
242 |
243 | This function takes _heavy_ inspiration from the excellent [preact-custom-element](https://github.com/preactjs/preact-custom-element). That library served as a starting point for this package, and all of the Preact guys deserve a massive dose of gratitude. I had slightly different needs, so decided to build this as part solution, part learning excersize.
244 |
--------------------------------------------------------------------------------
/packages/reactement/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | node: 'current',
8 | },
9 | },
10 | ],
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/packages/reactement/jest.config.js:
--------------------------------------------------------------------------------
1 | /* -----------------------------------
2 | *
3 | * Jest
4 | *
5 | * -------------------------------- */
6 |
7 | module.exports = {
8 | testEnvironment: 'jsdom',
9 | globals: { __DEV__: true },
10 | roots: [''],
11 | collectCoverage: true,
12 | collectCoverageFrom: ['/src/**/*.{ts,tsx}'],
13 | coverageDirectory: '/tests/coverage',
14 | coveragePathIgnorePatterns: ['/node_modules/', '(.*).d.ts'],
15 | setupFilesAfterEnv: ['/tests/setup.ts'],
16 | coverageThreshold: {
17 | global: {
18 | statements: 97,
19 | branches: 86,
20 | functions: 100,
21 | lines: 97,
22 | },
23 | },
24 | transform: {
25 | '^.+\\.tsx?$': 'ts-jest',
26 | '^.+\\js$': 'babel-jest',
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/packages/reactement/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactement",
3 | "version": "1.1.4",
4 | "author": "James Hill ",
5 | "homepage": "https://github.com/jahilldev/component-elements/tree/main/packages/reactement#readme",
6 | "license": "MIT",
7 | "main": "./dist/define.es5.js",
8 | "types": "./dist/define.d.ts",
9 | "engines": {
10 | "node": ">=10"
11 | },
12 | "exports": {
13 | ".": "./dist/define.js",
14 | "./es5": {
15 | "type": "./dist/define.d.ts",
16 | "import": "./dist/define.es5.js",
17 | "require": "./dist/define.es5.js"
18 | }
19 | },
20 | "typesVersions": {
21 | "*": {
22 | "es5": [
23 | "./dist/define.d.ts"
24 | ]
25 | }
26 | },
27 | "keywords": [
28 | "react",
29 | "custom elements",
30 | "web components",
31 | "virtual dom",
32 | "partial hydration",
33 | "universal",
34 | "isomorphic",
35 | "hydrate",
36 | "component"
37 | ],
38 | "directories": {
39 | "lib": "dist",
40 | "test": "tests"
41 | },
42 | "files": [
43 | "dist"
44 | ],
45 | "repository": {
46 | "type": "git",
47 | "url": "git+https://github.com/jahilldev/component-elements.git"
48 | },
49 | "scripts": {
50 | "start": "run-s clean watch",
51 | "build": "webpack --mode=production",
52 | "watch": "webpack --watch",
53 | "clean": "rimraf ./dist",
54 | "lint": "eslint",
55 | "test": "jest"
56 | },
57 | "bugs": {
58 | "url": "https://github.com/jahilldev/component-elements/issues"
59 | },
60 | "peerDependencies": {
61 | "react": ">=16",
62 | "react-dom": ">=16"
63 | },
64 | "devDependencies": {
65 | "@component-elements/shared": "1.0.0",
66 | "@types/enzyme": "^3.10.12",
67 | "enzyme": "^3.11.0",
68 | "enzyme-adapter-react-16": "^1.15.7",
69 | "react": "^16.14.0",
70 | "react-dom": "^16.14.0"
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/packages/reactement/src/define.ts:
--------------------------------------------------------------------------------
1 | import React, { createElement, ComponentFactory, FunctionComponent } from 'react';
2 | import { render } from 'react-dom';
3 | import {
4 | IProps,
5 | ErrorTypes,
6 | CustomElement,
7 | isPromise,
8 | parseJson,
9 | getElementTag,
10 | getPropKey,
11 | getElementAttributes,
12 | getAsyncComponent,
13 | } from '@component-elements/shared';
14 | import { parseHtml } from './parse';
15 | import { IOptions, ComponentFunction } from './model';
16 |
17 | /* -----------------------------------
18 | *
19 | * Define
20 | *
21 | * -------------------------------- */
22 |
23 | function define(
24 | tagName: string,
25 | child: ComponentFunction
,
26 | options: IOptions = {}
27 | ): FunctionComponent
{
28 | const { wrapComponent } = options;
29 | const preRender = typeof window === 'undefined';
30 | const elementTag = getElementTag(tagName);
31 |
32 | if (!preRender) {
33 | customElements.define(elementTag, setupElement(child, options));
34 |
35 | return;
36 | }
37 |
38 | const content = child();
39 |
40 | if (isPromise(content)) {
41 | throw new Error(`${ErrorTypes.Promise} : <${tagName}>`);
42 | }
43 |
44 | let component = content;
45 |
46 | if (wrapComponent) {
47 | component = wrapComponent(content);
48 | }
49 |
50 | return (props: P) =>
51 | createElement(elementTag, { server: true }, [
52 | createElement('script', {
53 | type: 'application/json',
54 | dangerouslySetInnerHTML: { __html: JSON.stringify(props) },
55 | }),
56 | createElement(component, props),
57 | ]);
58 | }
59 |
60 | /* -----------------------------------
61 | *
62 | * Setup
63 | *
64 | * -------------------------------- */
65 |
66 | function setupElement(component: ComponentFunction, options: IOptions = {}): any {
67 | const { attributes = [] } = options;
68 |
69 | if (typeof Reflect !== 'undefined' && Reflect.construct) {
70 | const CustomElement = function () {
71 | const element = Reflect.construct(HTMLElement, [], CustomElement);
72 |
73 | element.__mounted = false;
74 | element.__component = component;
75 | element.__properties = {};
76 | element.__slots = {};
77 | element.__children = void 0;
78 | element.__options = options;
79 |
80 | return element;
81 | };
82 |
83 | CustomElement.observedAttributes = ['props', ...attributes];
84 |
85 | CustomElement.prototype = Object.create(HTMLElement.prototype);
86 | CustomElement.prototype.constructor = CustomElement;
87 | CustomElement.prototype.connectedCallback = onConnected;
88 | CustomElement.prototype.attributeChangedCallback = onAttributeChange;
89 | CustomElement.prototype.disconnectedCallback = onDisconnected;
90 |
91 | return CustomElement;
92 | }
93 |
94 | return class CustomElement extends HTMLElement {
95 | __mounted = false;
96 | __component = component;
97 | __properties = {};
98 | __slots = {};
99 | __children = void 0;
100 | __options = options;
101 |
102 | static observedAttributes = ['props', ...attributes];
103 |
104 | public connectedCallback() {
105 | onConnected.call(this);
106 | }
107 |
108 | public attributeChangedCallback(...args) {
109 | onAttributeChange.call(this, ...args);
110 | }
111 |
112 | public disconnectedCallback() {
113 | onDisconnected.call(this);
114 | }
115 | };
116 | }
117 |
118 | /* -----------------------------------
119 | *
120 | * Connected
121 | *
122 | * -------------------------------- */
123 |
124 | function onConnected(this: CustomElement) {
125 | const attributes = getElementAttributes.call(this);
126 | const props = this.getAttribute('props');
127 | const json = this.querySelector('[type="application/json"]');
128 | const data = parseJson.call(this, props || json?.innerHTML || '{}');
129 |
130 | json?.remove();
131 |
132 | let children = this.__children;
133 |
134 | if (!this.__mounted && !this.hasAttribute('server')) {
135 | children = createElement(parseHtml.call(this), {});
136 | }
137 |
138 | this.__properties = { ...this.__slots, ...data, ...attributes };
139 | this.__children = children || [];
140 |
141 | this.removeAttribute('server');
142 | this.innerHTML = '';
143 |
144 | const response = this.__component();
145 |
146 | const renderer = (result: ComponentFactory) =>
147 | finaliseComponent.call(this, result);
148 |
149 | if (isPromise(response)) {
150 | getAsyncComponent(response, this.tagName).then(renderer);
151 |
152 | return;
153 | }
154 |
155 | renderer(response);
156 | }
157 |
158 | /* -----------------------------------
159 | *
160 | * Attribute
161 | *
162 | * -------------------------------- */
163 |
164 | function onAttributeChange(this: CustomElement, name: string, original: string, updated: string) {
165 | if (!this.__mounted) {
166 | return;
167 | }
168 |
169 | updated = updated == null ? void 0 : updated;
170 |
171 | let props = this.__properties;
172 |
173 | if (name === 'props') {
174 | props = { ...props, ...parseJson.call(this, updated) };
175 | } else {
176 | props[getPropKey(name)] = updated;
177 | }
178 |
179 | this.__properties = props;
180 |
181 | render(
182 | createElement(this.__instance, { ...props, parent: this, children: this.__children }),
183 | this
184 | );
185 | }
186 |
187 | /* -----------------------------------
188 | *
189 | * Disconnected
190 | *
191 | * -------------------------------- */
192 |
193 | function onDisconnected(this: CustomElement) {
194 | render(null, this);
195 | }
196 |
197 | /* -----------------------------------
198 | *
199 | * Finalise
200 | *
201 | * -------------------------------- */
202 |
203 | function finaliseComponent(this: CustomElement, component: ComponentFactory) {
204 | const { tagName } = this;
205 | const { wrapComponent } = this.__options;
206 |
207 | if (!component) {
208 | console.error(ErrorTypes.Missing, `: <${tagName.toLowerCase()}>`);
209 |
210 | return;
211 | }
212 |
213 | if (wrapComponent) {
214 | component = wrapComponent(component);
215 | }
216 |
217 | this.__instance = component;
218 | this.__mounted = true;
219 |
220 | const props = {
221 | ...this.__properties,
222 | parent: this,
223 | children: this.__children,
224 | };
225 |
226 | render(createElement(component, props), this);
227 | }
228 |
229 | /* -----------------------------------
230 | *
231 | * Export
232 | *
233 | * -------------------------------- */
234 |
235 | export { define };
236 |
--------------------------------------------------------------------------------
/packages/reactement/src/model.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFactory } from 'react';
2 |
3 | /* -----------------------------------
4 | *
5 | * Types
6 | *
7 | * -------------------------------- */
8 |
9 | type ComponentFunction = () => ComponentResult
;
10 | type ComponentResult
= ComponentFactory
| ComponentAsync
;
11 | type ComponentAsync
=
12 | | Promise>
13 | | Promise<{ [index: string]: ComponentFactory }>;
14 |
15 | /* -----------------------------------
16 | *
17 | * IOptions
18 | *
19 | * -------------------------------- */
20 |
21 | interface IOptions {
22 | attributes?: string[];
23 | formatProps?:
(props: P) => P;
24 | wrapComponent?:
(child: ComponentFactory
) => any;
25 | }
26 |
27 | /* -----------------------------------
28 | *
29 | * Export
30 | *
31 | * -------------------------------- */
32 |
33 | export { IOptions, ComponentFunction, ComponentResult, ComponentAsync };
34 |
--------------------------------------------------------------------------------
/packages/reactement/src/parse.ts:
--------------------------------------------------------------------------------
1 | import React, { createElement, ComponentFactory, Fragment } from 'react';
2 | import {
3 | CustomElement,
4 | getDocument,
5 | getAttributeObject,
6 | selfClosingTags,
7 | getPropKey,
8 | } from '@component-elements/shared';
9 |
10 | /* -----------------------------------
11 | *
12 | * parseHtml
13 | *
14 | * -------------------------------- */
15 |
16 | function parseHtml(this: CustomElement): ComponentFactory<{}, any> {
17 | const dom = getDocument(this.innerHTML);
18 |
19 | if (!dom) {
20 | return void 0;
21 | }
22 |
23 | const result = convertToVDom.call(this, dom);
24 |
25 | return () => result;
26 | }
27 |
28 | /* -----------------------------------
29 | *
30 | * convertToVDom
31 | *
32 | * -------------------------------- */
33 |
34 | function convertToVDom(this: CustomElement, node: Element) {
35 | if (node.nodeType === 3) {
36 | return node.textContent?.trim() || '';
37 | }
38 |
39 | if (node.nodeType !== 1) {
40 | return null;
41 | }
42 |
43 | const nodeName = String(node.nodeName).toLowerCase();
44 | const childNodes = Array.from(node.childNodes);
45 |
46 | const children = () => childNodes.map((child) => convertToVDom.call(this, child));
47 | const { slot, ...attributes } = getAttributeObject(node.attributes);
48 | const props = { ...attributes, key: Math.random() };
49 |
50 | if (nodeName === 'script') {
51 | return null;
52 | }
53 |
54 | if (nodeName === 'body') {
55 | return createElement(Fragment, { key: 'body' }, children());
56 | }
57 |
58 | if (selfClosingTags.includes(nodeName)) {
59 | return createElement(nodeName, props);
60 | }
61 |
62 | if (slot) {
63 | this.__slots[getPropKey(slot)] = getSlotChildren(children());
64 |
65 | return null;
66 | }
67 |
68 | return createElement(nodeName, props, children());
69 | }
70 |
71 | /* -----------------------------------
72 | *
73 | * getSlotChildren
74 | *
75 | * -------------------------------- */
76 |
77 | function getSlotChildren(children: JSX.Element[]) {
78 | const isString = (item) => typeof item === 'string';
79 |
80 | if (children.every(isString)) {
81 | return children.join(' ');
82 | }
83 |
84 | return createElement(Fragment, {}, children);
85 | }
86 |
87 | /* -----------------------------------
88 | *
89 | * Export
90 | *
91 | * -------------------------------- */
92 |
93 | export { parseHtml };
94 |
--------------------------------------------------------------------------------
/packages/reactement/tests/define.spec.tsx:
--------------------------------------------------------------------------------
1 | import React, { createElement, Fragment, ComponentFactory } from 'react';
2 | import { mount, shallow } from 'enzyme';
3 | import { define } from '../src/define';
4 |
5 | /* -----------------------------------
6 | *
7 | * Promises
8 | *
9 | * -------------------------------- */
10 |
11 | function flushPromises() {
12 | return new Promise((resolve) => setTimeout(resolve, 0));
13 | }
14 |
15 | /* -----------------------------------
16 | *
17 | * IProps
18 | *
19 | * -------------------------------- */
20 |
21 | interface IProps {
22 | customTitle?: string;
23 | value: string;
24 | children?: any;
25 | }
26 |
27 | /* -----------------------------------
28 | *
29 | * Component
30 | *
31 | * -------------------------------- */
32 |
33 | function Message({ customTitle, value, children }: IProps) {
34 | return (
35 |
36 | {customTitle && {customTitle} }
37 | {value}
38 | {children}
39 |
40 | );
41 | }
42 |
43 | /* -----------------------------------
44 | *
45 | * Define
46 | *
47 | * -------------------------------- */
48 |
49 | describe('define()', () => {
50 | const { document } = globalThis;
51 | const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
52 |
53 | describe('when run in the browser', () => {
54 | let root;
55 |
56 | beforeEach(() => {
57 | root = document.createElement('div');
58 | document.body.appendChild(root);
59 | });
60 |
61 | afterEach(() => {
62 | document.body.removeChild(root);
63 | });
64 |
65 | it('validates tag name value with prefix if needed', () => {
66 | const props = { value: 'propsValue' };
67 |
68 | define('message', () => Message);
69 |
70 | const element = document.createElement('component-message');
71 |
72 | element.setAttribute('props', JSON.stringify(props));
73 |
74 | root.appendChild(element);
75 |
76 | expect(root.innerHTML).toContain(`${props.value} `);
77 | });
78 |
79 | it('renders component correctly when from props attribute', async () => {
80 | const props = { value: 'propsValue' };
81 |
82 | define('message-one', () => Message);
83 |
84 | const element = document.createElement('message-one');
85 |
86 | element.setAttribute('props', JSON.stringify(props));
87 |
88 | root.appendChild(element);
89 |
90 | expect(root.innerHTML).toContain(`${props.value} `);
91 | });
92 |
93 | it('renders component correctly when from json script block', async () => {
94 | const props = { value: 'jsonValue' };
95 | const json = ``;
96 |
97 | define('message-two', () => Message);
98 |
99 | const element = document.createElement('message-two');
100 |
101 | element.innerHTML = json;
102 |
103 | root.appendChild(element);
104 |
105 | expect(root.innerHTML).toContain(`${props.value} `);
106 | });
107 |
108 | it('sets contained HTML as children prop when not server rendered', async () => {
109 | const props = { value: 'childMarkup' };
110 | const json = ``;
111 | const html = '
Testing
Click here ';
112 |
113 | define('message-three', () => Message);
114 |
115 | const element = document.createElement('message-three');
116 |
117 | element.innerHTML = json + html;
118 |
119 | root.appendChild(element);
120 |
121 | expect(root.innerHTML).toContain(`${props.value} ${html}`);
122 | });
123 |
124 | it('does not use contained HTML if server rendered', async () => {
125 | const props = { value: 'serverRender' };
126 | const json = ``;
127 | const html = 'Server rendered!
Click here ';
128 |
129 | define('message-four', () => Message);
130 |
131 | const element = document.createElement('message-four');
132 |
133 | element.setAttribute('server', '');
134 | element.innerHTML = json + html;
135 |
136 | root.appendChild(element);
137 |
138 | expect(root.innerHTML).toContain(`${props.value} `);
139 | });
140 |
141 | it('renders component asynchronously when provided', async () => {
142 | const props = { value: 'asyncValue' };
143 | const json = ``;
144 |
145 | define('message-five', () => Promise.resolve(Message));
146 |
147 | const element = document.createElement('message-five');
148 |
149 | element.innerHTML = json;
150 |
151 | root.appendChild(element);
152 |
153 | await flushPromises();
154 |
155 | expect(root.innerHTML).toContain(`${props.value} `);
156 | });
157 |
158 | it('tries to infer the component if not explicitly returned', async () => {
159 | const props = { value: 'inferValue' };
160 | const json = ``;
161 |
162 | define('message-six', () => Promise.resolve({ MessageSix: Message }));
163 |
164 | const element = document.createElement('message-six');
165 |
166 | element.innerHTML = json;
167 |
168 | root.appendChild(element);
169 |
170 | await flushPromises();
171 |
172 | expect(root.innerHTML).toContain(`${props.value} `);
173 | });
174 |
175 | it('merges defined attributes in array with component props', () => {
176 | const customTitle = 'customTitle';
177 | const props = { value: 'attrProps' };
178 | const json = ``;
179 |
180 | define('message-seven', () => Message, { attributes: ['custom-title'] });
181 |
182 | const element = document.createElement('message-seven');
183 |
184 | element.setAttribute('custom-title', customTitle);
185 | element.innerHTML = json;
186 |
187 | root.appendChild(element);
188 |
189 | expect(root.innerHTML).toContain(`${customTitle} ${props.value} `);
190 | });
191 |
192 | it('errors if component cannot be found in function', async () => {
193 | define('message-eight', () => Promise.resolve({ Message }));
194 |
195 | const element = document.createElement('message-eight');
196 |
197 | root.appendChild(element);
198 |
199 | await flushPromises();
200 |
201 | expect(errorSpy).toBeCalled();
202 | expect(element.innerHTML).toEqual('');
203 | });
204 |
205 | it('updates component props when attributes are changed', () => {
206 | const customTitle = 'customTitle';
207 | const updatedProp = 'updated!';
208 | const props = { value: 'attrUpdate' };
209 | const html = 'Click here ';
210 |
211 | define('message-nine', () => Message, { attributes: ['custom-title'] });
212 |
213 | const element = document.createElement('message-nine');
214 |
215 | element.setAttribute('custom-title', customTitle);
216 | element.setAttribute('props', JSON.stringify(props));
217 |
218 | element.innerHTML = html;
219 |
220 | root.appendChild(element);
221 |
222 | expect(root.innerHTML).toContain(`${customTitle} ${props.value} ${html}`);
223 |
224 | element.setAttribute('custom-title', '');
225 | element.setAttribute('props', JSON.stringify({ ...props, value: updatedProp }));
226 |
227 | expect(root.innerHTML).toContain(`${updatedProp} ${html}`);
228 | });
229 |
230 | it('wraps component in an HOC if provided', () => {
231 | const props = { value: 'wrapComponent' };
232 | const json = ``;
233 |
234 | const wrapComponent = (child: ComponentFactory) => (props: any) =>
235 | createElement('section', {}, createElement(child, props));
236 |
237 | define('message-ten', () => Message, { wrapComponent });
238 |
239 | const element = document.createElement('message-ten');
240 |
241 | element.innerHTML = json;
242 |
243 | root.appendChild(element);
244 |
245 | expect(root.innerHTML).toContain(``);
246 | });
247 |
248 | it('correctly passes props through formatProps if provided', () => {
249 | const props = { Value: 'formatProps' };
250 | const json = ``;
251 |
252 | const formatProps = (props: any) => {
253 | const keys = Object.keys(props);
254 |
255 | return keys.reduce((result, key) => {
256 | result[key.toLowerCase()] = props[key];
257 |
258 | return result;
259 | }, {});
260 | };
261 |
262 | define('message-eleven', () => Message, { formatProps });
263 |
264 | const element = document.createElement('message-eleven');
265 |
266 | element.innerHTML = json;
267 |
268 | root.appendChild(element);
269 |
270 | expect(root.innerHTML).toContain(`${props.Value} `);
271 | });
272 |
273 | it('correctly segments <* slot="{key}" /> elements into props', () => {
274 | const customTitle = 'customTitle ';
275 | const html = `${customTitle}
`;
276 |
277 | define('message-twelve', () => Message);
278 |
279 | const element = document.createElement('message-twelve');
280 |
281 | element.innerHTML = html;
282 |
283 | root.appendChild(element);
284 |
285 | expect(root.innerHTML).toContain(`${customTitle} `);
286 | });
287 |
288 | it('correctly caches children when moved in the DOM', () => {
289 | const customTitle = 'customTitle ';
290 | const customText = 'Lorem ipsum dolor';
291 | const html = `${customTitle}
${customText}
`;
292 |
293 | define('message-thirteen', () => Message);
294 |
295 | const element = document.createElement('message-thirteen');
296 | const wrapper = document.createElement('main');
297 |
298 | element.innerHTML = html;
299 |
300 | root.appendChild(element);
301 |
302 | element.remove();
303 |
304 | expect(root.innerHTML).toContain('');
305 |
306 | root.appendChild(wrapper);
307 | wrapper.appendChild(element);
308 |
309 | expect(root.innerHTML).toContain(`${customTitle} ${customText}
`);
310 | });
311 | });
312 |
313 | describe('when run in the browser (no "Reflect.construct")', () => {
314 | const { document, Reflect } = globalThis;
315 | let root;
316 |
317 | beforeAll(() => {
318 | delete (globalThis as any).Reflect;
319 | });
320 |
321 | beforeEach(() => {
322 | root = document.createElement('div');
323 | document.body.appendChild(root);
324 | });
325 |
326 | afterEach(() => {
327 | document.body.removeChild(root);
328 | });
329 |
330 | afterAll(() => {
331 | globalThis.Reflect = Reflect;
332 | });
333 |
334 | it('renders component correctly', () => {
335 | const customTitle = 'customTitle';
336 | const props = { value: 'attrUpdate' };
337 | const json = ``;
338 | const html = 'Click here ';
339 |
340 | define('message-class', () => Message, { attributes: ['custom-title'] });
341 |
342 | const element = document.createElement('message-class');
343 |
344 | element.setAttribute('custom-title', customTitle);
345 | element.innerHTML = json + html;
346 |
347 | root.appendChild(element);
348 |
349 | expect(root.innerHTML).toContain(`${customTitle} ${props.value} ${html}`);
350 |
351 | element.setAttribute('custom-title', '');
352 |
353 | expect(root.innerHTML).toContain(`${props.value} ${html}`);
354 | });
355 | });
356 |
357 | describe('when run on the server', () => {
358 | const { window } = globalThis;
359 |
360 | beforeAll(() => {
361 | delete (globalThis as any).window;
362 | });
363 |
364 | afterAll(() => {
365 | globalThis.window = window;
366 | });
367 |
368 | it('returns the correct markup', () => {
369 | const props = { value: 'serverValue' };
370 | const component = define('message-one', () => Message);
371 |
372 | const instance = shallow(createElement(component, props));
373 |
374 | expect(instance.find('message-one').length).toEqual(1);
375 | expect(instance.find('message-one').prop('server')).toEqual(true);
376 | });
377 |
378 | it('throws an error when used with a promise', () => {
379 | expect(() => define('message-two', () => Promise.resolve(Message))).toThrow();
380 | });
381 |
382 | // it('includes a json script block with props', () => {
383 | // const props = { value: 'serverValue' };
384 | // const component = define('message-three', () => Message);
385 |
386 | // const instance = shallow(createElement(component, props) as any);
387 |
388 | // expect(instance.find('script').text()).toEqual(JSON.stringify(props));
389 | // });
390 | });
391 | });
392 |
--------------------------------------------------------------------------------
/packages/reactement/tests/parse.spec.ts:
--------------------------------------------------------------------------------
1 | import React, { createElement } from 'react';
2 | import { mount } from 'enzyme';
3 | import { parseHtml } from '../src/parse';
4 |
5 | /* -----------------------------------
6 | *
7 | * Variables
8 | *
9 | * -------------------------------- */
10 |
11 | const testHeading = 'testHeading';
12 | const testWhitespace = ' ';
13 | const testHtml = `${testHeading} `;
14 | const testScript = ``;
15 | const testData = { testHeading };
16 |
17 | /* -----------------------------------
18 | *
19 | * Parse
20 | *
21 | * -------------------------------- */
22 |
23 | describe('parse', () => {
24 | describe('parseHtml()', () => {
25 | it('should correctly handle misformed html', () => {
26 | const testText = 'testText';
27 | const result = parseHtml.call({ innerHTML: `${testText}` });
28 | const instance = mount(createElement(result, {}) as any);
29 |
30 | expect(instance.html()).toEqual(`${testText} `);
31 | });
32 |
33 | it('handles text values witin custom element', () => {
34 | const result = parseHtml.call({ innerHTML: testHeading });
35 | const instance = mount(createElement(result, {}) as any);
36 |
37 | expect(instance.text()).toEqual(testHeading);
38 | });
39 |
40 | it('handles whitespace within custom element', () => {
41 | const result = parseHtml.call({ innerHTML: testWhitespace });
42 | const instance = mount(createElement(result, {}) as any);
43 |
44 | expect(instance.text()).toEqual('');
45 | expect(instance.html()).toEqual('');
46 | });
47 |
48 | it('removes script blocks for security', () => {
49 | const result = parseHtml.call({ innerHTML: testScript });
50 | const instance = mount(createElement(result, {}) as any);
51 |
52 | expect(instance.text()).toEqual('');
53 | });
54 |
55 | it('correctly converts an HTML string into a VDom tree', () => {
56 | const result = parseHtml.call({ innerHTML: testHtml });
57 | const instance = mount(createElement(result, {}) as any);
58 |
59 | expect(instance.find('h1').text()).toEqual(testHeading);
60 | });
61 |
62 | it('should remove <* slot="{key}"> and apply to props', () => {
63 | const slots = {};
64 | const slotKey = 'slotKey';
65 | const slotValue = 'slotValue';
66 |
67 | const slotHtml = `${slotValue} `;
68 | const headingHtml = `${testHeading} `;
69 | const testHtml = `${headingHtml}${slotHtml} `;
70 |
71 | const result = parseHtml.call({ innerHTML: testHtml, __slots: slots });
72 | const instance = mount(createElement(result, {}) as any);
73 |
74 | expect(instance.html()).toEqual(``);
75 | expect(slots).toEqual({ [slotKey]: slotValue });
76 | });
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/packages/reactement/tests/setup.ts:
--------------------------------------------------------------------------------
1 | import { configure } from 'enzyme';
2 | import Adapter from 'enzyme-adapter-react-16';
3 |
4 | /* -----------------------------------
5 | *
6 | * Setup
7 | *
8 | * -------------------------------- */
9 |
10 | configure({ adapter: new Adapter() });
11 |
--------------------------------------------------------------------------------
/packages/reactement/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "jsx": "react"
6 | },
7 | "include": ["./src"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/reactement/webpack.config.ts:
--------------------------------------------------------------------------------
1 | import { Configuration, DefinePlugin } from 'webpack';
2 | import nodeExternals from 'webpack-node-externals';
3 | import * as path from 'path';
4 |
5 | /* -----------------------------------
6 | *
7 | * Output
8 | *
9 | * -------------------------------- */
10 |
11 | const outputFiles = [
12 | { target: 'es5', filename: '[name].es5.js' },
13 | { target: 'es2016', filename: '[name].js' },
14 | ];
15 |
16 | /* -----------------------------------
17 | *
18 | * Default
19 | *
20 | * -------------------------------- */
21 |
22 | const defaultConfig = {
23 | entry: {
24 | define: path.join(__dirname, './src/define.ts'),
25 | },
26 | externals: [
27 | nodeExternals({
28 | allowlist: ['@component-elements/shared'],
29 | modulesDir: path.resolve(__dirname, '../../node_modules'),
30 | }),
31 | ],
32 | context: path.join(__dirname, './src'),
33 | output: {
34 | path: path.join(__dirname, './dist'),
35 | filename: '[name].js',
36 | libraryTarget: 'umd',
37 | globalObject: 'this',
38 | chunkFormat: 'commonjs',
39 | },
40 | resolve: {
41 | extensions: ['.js', '.ts', '.tsx', 'json'],
42 | },
43 | node: {
44 | __filename: true,
45 | __dirname: true,
46 | },
47 | stats: {
48 | colors: true,
49 | timings: true,
50 | },
51 | };
52 |
53 | /* -----------------------------------
54 | *
55 | * Config
56 | *
57 | * -------------------------------- */
58 |
59 | const config = ({ mode }): Configuration[] =>
60 | outputFiles.map(({ target, filename, ...config }) => ({
61 | ...defaultConfig,
62 | mode,
63 | target,
64 | devtool: mode === 'development' ? 'eval-source-map' : void 0,
65 | cache: mode === 'development',
66 | output: {
67 | ...defaultConfig.output,
68 | filename,
69 | },
70 | module: {
71 | rules: [
72 | {
73 | test: /\.ts$/,
74 | use: [
75 | {
76 | loader: 'ts-loader',
77 | options: {
78 | compilerOptions: {
79 | target,
80 | },
81 | },
82 | },
83 | ],
84 | },
85 | {
86 | test: /\.m?js$/,
87 | use: {
88 | loader: 'babel-loader',
89 | options: {
90 | ...(target === 'es5' && { presets: ['@babel/preset-env'] }),
91 | },
92 | },
93 | },
94 | ],
95 | },
96 | performance: {
97 | hints: mode === 'development' ? 'warning' : void 0,
98 | },
99 | plugins: [
100 | new DefinePlugin({
101 | __DEV__: mode === 'development',
102 | }),
103 | ],
104 | ...config,
105 | }));
106 |
107 | /* -----------------------------------
108 | *
109 | * Export
110 | *
111 | * -------------------------------- */
112 |
113 | module.exports = config;
114 |
--------------------------------------------------------------------------------
/packages/reactement/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@types/cheerio@*":
6 | "integrity" "sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw=="
7 | "resolved" "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.31.tgz"
8 | "version" "0.22.31"
9 | dependencies:
10 | "@types/node" "*"
11 |
12 | "@types/enzyme@^3.10.11":
13 | "integrity" "sha512-LEtC7zXsQlbGXWGcnnmOI7rTyP+i1QzQv4Va91RKXDEukLDaNyxu0rXlfMiGEhJwfgTPCTb0R+Pnlj//oM9e/w=="
14 | "resolved" "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.11.tgz"
15 | "version" "3.10.11"
16 | dependencies:
17 | "@types/cheerio" "*"
18 | "@types/react" "*"
19 |
20 | "@types/node@*":
21 | "integrity" "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA=="
22 | "resolved" "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz"
23 | "version" "17.0.18"
24 |
25 | "@types/prop-types@*":
26 | "integrity" "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
27 | "resolved" "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz"
28 | "version" "15.7.4"
29 |
30 | "@types/react@*":
31 | "integrity" "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug=="
32 | "resolved" "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz"
33 | "version" "17.0.39"
34 | dependencies:
35 | "@types/prop-types" "*"
36 | "@types/scheduler" "*"
37 | "csstype" "^3.0.2"
38 |
39 | "@types/scheduler@*":
40 | "integrity" "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
41 | "resolved" "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz"
42 | "version" "0.16.2"
43 |
44 | "airbnb-prop-types@^2.16.0":
45 | "integrity" "sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg=="
46 | "resolved" "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz"
47 | "version" "2.16.0"
48 | dependencies:
49 | "array.prototype.find" "^2.1.1"
50 | "function.prototype.name" "^1.1.2"
51 | "is-regex" "^1.1.0"
52 | "object-is" "^1.1.2"
53 | "object.assign" "^4.1.0"
54 | "object.entries" "^1.1.2"
55 | "prop-types" "^15.7.2"
56 | "prop-types-exact" "^1.2.0"
57 | "react-is" "^16.13.1"
58 |
59 | "array.prototype.filter@^1.0.0":
60 | "integrity" "sha512-Dk3Ty7N42Odk7PjU/Ci3zT4pLj20YvuVnneG/58ICM6bt4Ij5kZaJTVQ9TSaWaIECX2sFyz4KItkVZqHNnciqw=="
61 | "resolved" "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.1.tgz"
62 | "version" "1.0.1"
63 | dependencies:
64 | "call-bind" "^1.0.2"
65 | "define-properties" "^1.1.3"
66 | "es-abstract" "^1.19.0"
67 | "es-array-method-boxes-properly" "^1.0.0"
68 | "is-string" "^1.0.7"
69 |
70 | "array.prototype.find@^2.1.1":
71 | "integrity" "sha512-sn40qmUiLYAcRb/1HsIQjTTZ1kCy8II8VtZJpMn2Aoen9twULhbWXisfh3HimGqMlHGUul0/TfKCnXg42LuPpQ=="
72 | "resolved" "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.2.0.tgz"
73 | "version" "2.2.0"
74 | dependencies:
75 | "call-bind" "^1.0.2"
76 | "define-properties" "^1.1.3"
77 | "es-abstract" "^1.19.4"
78 | "es-shim-unscopables" "^1.0.0"
79 |
80 | "array.prototype.flat@^1.2.3":
81 | "integrity" "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg=="
82 | "resolved" "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz"
83 | "version" "1.2.5"
84 | dependencies:
85 | "call-bind" "^1.0.2"
86 | "define-properties" "^1.1.3"
87 | "es-abstract" "^1.19.0"
88 |
89 | "boolbase@^1.0.0":
90 | "integrity" "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
91 | "resolved" "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz"
92 | "version" "1.0.0"
93 |
94 | "call-bind@^1.0.0", "call-bind@^1.0.2":
95 | "integrity" "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA=="
96 | "resolved" "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz"
97 | "version" "1.0.2"
98 | dependencies:
99 | "function-bind" "^1.1.1"
100 | "get-intrinsic" "^1.0.2"
101 |
102 | "cheerio-select@^1.5.0":
103 | "integrity" "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg=="
104 | "resolved" "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz"
105 | "version" "1.5.0"
106 | dependencies:
107 | "css-select" "^4.1.3"
108 | "css-what" "^5.0.1"
109 | "domelementtype" "^2.2.0"
110 | "domhandler" "^4.2.0"
111 | "domutils" "^2.7.0"
112 |
113 | "cheerio@^1.0.0-rc.3":
114 | "integrity" "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw=="
115 | "resolved" "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz"
116 | "version" "1.0.0-rc.10"
117 | dependencies:
118 | "cheerio-select" "^1.5.0"
119 | "dom-serializer" "^1.3.2"
120 | "domhandler" "^4.2.0"
121 | "htmlparser2" "^6.1.0"
122 | "parse5" "^6.0.1"
123 | "parse5-htmlparser2-tree-adapter" "^6.0.1"
124 | "tslib" "^2.2.0"
125 |
126 | "commander@^2.19.0":
127 | "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
128 | "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz"
129 | "version" "2.20.3"
130 |
131 | "css-select@^4.1.3":
132 | "integrity" "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ=="
133 | "resolved" "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz"
134 | "version" "4.2.1"
135 | dependencies:
136 | "boolbase" "^1.0.0"
137 | "css-what" "^5.1.0"
138 | "domhandler" "^4.3.0"
139 | "domutils" "^2.8.0"
140 | "nth-check" "^2.0.1"
141 |
142 | "css-what@^5.0.1", "css-what@^5.1.0":
143 | "integrity" "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw=="
144 | "resolved" "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz"
145 | "version" "5.1.0"
146 |
147 | "csstype@^3.0.2":
148 | "integrity" "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
149 | "resolved" "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz"
150 | "version" "3.0.10"
151 |
152 | "define-properties@^1.1.3":
153 | "integrity" "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ=="
154 | "resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz"
155 | "version" "1.1.3"
156 | dependencies:
157 | "object-keys" "^1.0.12"
158 |
159 | "discontinuous-range@1.0.0":
160 | "integrity" "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo="
161 | "resolved" "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz"
162 | "version" "1.0.0"
163 |
164 | "dom-serializer@^1.0.1", "dom-serializer@^1.3.2":
165 | "integrity" "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig=="
166 | "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz"
167 | "version" "1.3.2"
168 | dependencies:
169 | "domelementtype" "^2.0.1"
170 | "domhandler" "^4.2.0"
171 | "entities" "^2.0.0"
172 |
173 | "domelementtype@^2.0.1", "domelementtype@^2.2.0":
174 | "integrity" "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
175 | "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz"
176 | "version" "2.2.0"
177 |
178 | "domhandler@^4.0.0", "domhandler@^4.2.0", "domhandler@^4.3.0":
179 | "integrity" "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g=="
180 | "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz"
181 | "version" "4.3.0"
182 | dependencies:
183 | "domelementtype" "^2.2.0"
184 |
185 | "domutils@^2.5.2", "domutils@^2.7.0", "domutils@^2.8.0":
186 | "integrity" "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="
187 | "resolved" "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz"
188 | "version" "2.8.0"
189 | dependencies:
190 | "dom-serializer" "^1.0.1"
191 | "domelementtype" "^2.2.0"
192 | "domhandler" "^4.2.0"
193 |
194 | "entities@^2.0.0":
195 | "integrity" "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
196 | "resolved" "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz"
197 | "version" "2.2.0"
198 |
199 | "enzyme-adapter-react-16@^1.15.6":
200 | "integrity" "sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g=="
201 | "resolved" "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz"
202 | "version" "1.15.6"
203 | dependencies:
204 | "enzyme-adapter-utils" "^1.14.0"
205 | "enzyme-shallow-equal" "^1.0.4"
206 | "has" "^1.0.3"
207 | "object.assign" "^4.1.2"
208 | "object.values" "^1.1.2"
209 | "prop-types" "^15.7.2"
210 | "react-is" "^16.13.1"
211 | "react-test-renderer" "^16.0.0-0"
212 | "semver" "^5.7.0"
213 |
214 | "enzyme-adapter-utils@^1.14.0":
215 | "integrity" "sha512-F/z/7SeLt+reKFcb7597IThpDp0bmzcH1E9Oabqv+o01cID2/YInlqHbFl7HzWBl4h3OdZYedtwNDOmSKkk0bg=="
216 | "resolved" "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.0.tgz"
217 | "version" "1.14.0"
218 | dependencies:
219 | "airbnb-prop-types" "^2.16.0"
220 | "function.prototype.name" "^1.1.3"
221 | "has" "^1.0.3"
222 | "object.assign" "^4.1.2"
223 | "object.fromentries" "^2.0.3"
224 | "prop-types" "^15.7.2"
225 | "semver" "^5.7.1"
226 |
227 | "enzyme-shallow-equal@^1.0.1", "enzyme-shallow-equal@^1.0.4":
228 | "integrity" "sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q=="
229 | "resolved" "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz"
230 | "version" "1.0.4"
231 | dependencies:
232 | "has" "^1.0.3"
233 | "object-is" "^1.1.2"
234 |
235 | "enzyme@^3.0.0", "enzyme@^3.11.0":
236 | "integrity" "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw=="
237 | "resolved" "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz"
238 | "version" "3.11.0"
239 | dependencies:
240 | "array.prototype.flat" "^1.2.3"
241 | "cheerio" "^1.0.0-rc.3"
242 | "enzyme-shallow-equal" "^1.0.1"
243 | "function.prototype.name" "^1.1.2"
244 | "has" "^1.0.3"
245 | "html-element-map" "^1.2.0"
246 | "is-boolean-object" "^1.0.1"
247 | "is-callable" "^1.1.5"
248 | "is-number-object" "^1.0.4"
249 | "is-regex" "^1.0.5"
250 | "is-string" "^1.0.5"
251 | "is-subset" "^0.1.1"
252 | "lodash.escape" "^4.0.1"
253 | "lodash.isequal" "^4.5.0"
254 | "object-inspect" "^1.7.0"
255 | "object-is" "^1.0.2"
256 | "object.assign" "^4.1.0"
257 | "object.entries" "^1.1.1"
258 | "object.values" "^1.1.1"
259 | "raf" "^3.4.1"
260 | "rst-selector-parser" "^2.2.3"
261 | "string.prototype.trim" "^1.2.1"
262 |
263 | "es-abstract@^1.19.0", "es-abstract@^1.19.1", "es-abstract@^1.19.4":
264 | "integrity" "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA=="
265 | "resolved" "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz"
266 | "version" "1.19.5"
267 | dependencies:
268 | "call-bind" "^1.0.2"
269 | "es-to-primitive" "^1.2.1"
270 | "function-bind" "^1.1.1"
271 | "get-intrinsic" "^1.1.1"
272 | "get-symbol-description" "^1.0.0"
273 | "has" "^1.0.3"
274 | "has-symbols" "^1.0.3"
275 | "internal-slot" "^1.0.3"
276 | "is-callable" "^1.2.4"
277 | "is-negative-zero" "^2.0.2"
278 | "is-regex" "^1.1.4"
279 | "is-shared-array-buffer" "^1.0.2"
280 | "is-string" "^1.0.7"
281 | "is-weakref" "^1.0.2"
282 | "object-inspect" "^1.12.0"
283 | "object-keys" "^1.1.1"
284 | "object.assign" "^4.1.2"
285 | "string.prototype.trimend" "^1.0.4"
286 | "string.prototype.trimstart" "^1.0.4"
287 | "unbox-primitive" "^1.0.1"
288 |
289 | "es-array-method-boxes-properly@^1.0.0":
290 | "integrity" "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA=="
291 | "resolved" "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz"
292 | "version" "1.0.0"
293 |
294 | "es-shim-unscopables@^1.0.0":
295 | "integrity" "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w=="
296 | "resolved" "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz"
297 | "version" "1.0.0"
298 | dependencies:
299 | "has" "^1.0.3"
300 |
301 | "es-to-primitive@^1.2.1":
302 | "integrity" "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA=="
303 | "resolved" "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz"
304 | "version" "1.2.1"
305 | dependencies:
306 | "is-callable" "^1.1.4"
307 | "is-date-object" "^1.0.1"
308 | "is-symbol" "^1.0.2"
309 |
310 | "function-bind@^1.1.1":
311 | "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
312 | "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
313 | "version" "1.1.1"
314 |
315 | "function.prototype.name@^1.1.2", "function.prototype.name@^1.1.3":
316 | "integrity" "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA=="
317 | "resolved" "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz"
318 | "version" "1.1.5"
319 | dependencies:
320 | "call-bind" "^1.0.2"
321 | "define-properties" "^1.1.3"
322 | "es-abstract" "^1.19.0"
323 | "functions-have-names" "^1.2.2"
324 |
325 | "functions-have-names@^1.2.2":
326 | "integrity" "sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA=="
327 | "resolved" "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.2.tgz"
328 | "version" "1.2.2"
329 |
330 | "get-intrinsic@^1.0.2", "get-intrinsic@^1.1.0", "get-intrinsic@^1.1.1":
331 | "integrity" "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q=="
332 | "resolved" "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz"
333 | "version" "1.1.1"
334 | dependencies:
335 | "function-bind" "^1.1.1"
336 | "has" "^1.0.3"
337 | "has-symbols" "^1.0.1"
338 |
339 | "get-symbol-description@^1.0.0":
340 | "integrity" "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw=="
341 | "resolved" "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz"
342 | "version" "1.0.0"
343 | dependencies:
344 | "call-bind" "^1.0.2"
345 | "get-intrinsic" "^1.1.1"
346 |
347 | "has-bigints@^1.0.1":
348 | "integrity" "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="
349 | "resolved" "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz"
350 | "version" "1.0.1"
351 |
352 | "has-symbols@^1.0.1", "has-symbols@^1.0.2", "has-symbols@^1.0.3":
353 | "integrity" "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
354 | "resolved" "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz"
355 | "version" "1.0.3"
356 |
357 | "has-tostringtag@^1.0.0":
358 | "integrity" "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ=="
359 | "resolved" "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz"
360 | "version" "1.0.0"
361 | dependencies:
362 | "has-symbols" "^1.0.2"
363 |
364 | "has@^1.0.3":
365 | "integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw=="
366 | "resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
367 | "version" "1.0.3"
368 | dependencies:
369 | "function-bind" "^1.1.1"
370 |
371 | "html-element-map@^1.2.0":
372 | "integrity" "sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg=="
373 | "resolved" "https://registry.npmjs.org/html-element-map/-/html-element-map-1.3.1.tgz"
374 | "version" "1.3.1"
375 | dependencies:
376 | "array.prototype.filter" "^1.0.0"
377 | "call-bind" "^1.0.2"
378 |
379 | "htmlparser2@^6.1.0":
380 | "integrity" "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A=="
381 | "resolved" "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz"
382 | "version" "6.1.0"
383 | dependencies:
384 | "domelementtype" "^2.0.1"
385 | "domhandler" "^4.0.0"
386 | "domutils" "^2.5.2"
387 | "entities" "^2.0.0"
388 |
389 | "internal-slot@^1.0.3":
390 | "integrity" "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA=="
391 | "resolved" "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz"
392 | "version" "1.0.3"
393 | dependencies:
394 | "get-intrinsic" "^1.1.0"
395 | "has" "^1.0.3"
396 | "side-channel" "^1.0.4"
397 |
398 | "is-bigint@^1.0.1":
399 | "integrity" "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg=="
400 | "resolved" "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz"
401 | "version" "1.0.4"
402 | dependencies:
403 | "has-bigints" "^1.0.1"
404 |
405 | "is-boolean-object@^1.0.1", "is-boolean-object@^1.1.0":
406 | "integrity" "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA=="
407 | "resolved" "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz"
408 | "version" "1.1.2"
409 | dependencies:
410 | "call-bind" "^1.0.2"
411 | "has-tostringtag" "^1.0.0"
412 |
413 | "is-callable@^1.1.4", "is-callable@^1.1.5", "is-callable@^1.2.4":
414 | "integrity" "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="
415 | "resolved" "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz"
416 | "version" "1.2.4"
417 |
418 | "is-date-object@^1.0.1":
419 | "integrity" "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ=="
420 | "resolved" "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz"
421 | "version" "1.0.5"
422 | dependencies:
423 | "has-tostringtag" "^1.0.0"
424 |
425 | "is-negative-zero@^2.0.2":
426 | "integrity" "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA=="
427 | "resolved" "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz"
428 | "version" "2.0.2"
429 |
430 | "is-number-object@^1.0.4":
431 | "integrity" "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g=="
432 | "resolved" "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz"
433 | "version" "1.0.6"
434 | dependencies:
435 | "has-tostringtag" "^1.0.0"
436 |
437 | "is-regex@^1.0.5", "is-regex@^1.1.0", "is-regex@^1.1.4":
438 | "integrity" "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg=="
439 | "resolved" "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz"
440 | "version" "1.1.4"
441 | dependencies:
442 | "call-bind" "^1.0.2"
443 | "has-tostringtag" "^1.0.0"
444 |
445 | "is-shared-array-buffer@^1.0.2":
446 | "integrity" "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA=="
447 | "resolved" "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz"
448 | "version" "1.0.2"
449 | dependencies:
450 | "call-bind" "^1.0.2"
451 |
452 | "is-string@^1.0.5", "is-string@^1.0.7":
453 | "integrity" "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg=="
454 | "resolved" "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz"
455 | "version" "1.0.7"
456 | dependencies:
457 | "has-tostringtag" "^1.0.0"
458 |
459 | "is-subset@^0.1.1":
460 | "integrity" "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY="
461 | "resolved" "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz"
462 | "version" "0.1.1"
463 |
464 | "is-symbol@^1.0.2", "is-symbol@^1.0.3":
465 | "integrity" "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg=="
466 | "resolved" "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz"
467 | "version" "1.0.4"
468 | dependencies:
469 | "has-symbols" "^1.0.2"
470 |
471 | "is-weakref@^1.0.2":
472 | "integrity" "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ=="
473 | "resolved" "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz"
474 | "version" "1.0.2"
475 | dependencies:
476 | "call-bind" "^1.0.2"
477 |
478 | "js-tokens@^3.0.0 || ^4.0.0":
479 | "integrity" "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
480 | "resolved" "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
481 | "version" "4.0.0"
482 |
483 | "lodash.escape@^4.0.1":
484 | "integrity" "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg="
485 | "resolved" "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz"
486 | "version" "4.0.1"
487 |
488 | "lodash.flattendeep@^4.4.0":
489 | "integrity" "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI="
490 | "resolved" "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz"
491 | "version" "4.4.0"
492 |
493 | "lodash.isequal@^4.5.0":
494 | "integrity" "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
495 | "resolved" "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz"
496 | "version" "4.5.0"
497 |
498 | "loose-envify@^1.1.0", "loose-envify@^1.4.0":
499 | "integrity" "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="
500 | "resolved" "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
501 | "version" "1.4.0"
502 | dependencies:
503 | "js-tokens" "^3.0.0 || ^4.0.0"
504 |
505 | "moo@^0.5.0":
506 | "integrity" "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w=="
507 | "resolved" "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz"
508 | "version" "0.5.1"
509 |
510 | "nearley@^2.7.10":
511 | "integrity" "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ=="
512 | "resolved" "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz"
513 | "version" "2.20.1"
514 | dependencies:
515 | "commander" "^2.19.0"
516 | "moo" "^0.5.0"
517 | "railroad-diagrams" "^1.0.0"
518 | "randexp" "0.4.6"
519 |
520 | "nth-check@^2.0.1":
521 | "integrity" "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w=="
522 | "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz"
523 | "version" "2.0.1"
524 | dependencies:
525 | "boolbase" "^1.0.0"
526 |
527 | "object-assign@^4.1.1":
528 | "integrity" "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
529 | "resolved" "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
530 | "version" "4.1.1"
531 |
532 | "object-inspect@^1.12.0", "object-inspect@^1.7.0", "object-inspect@^1.9.0":
533 | "integrity" "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
534 | "resolved" "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz"
535 | "version" "1.12.0"
536 |
537 | "object-is@^1.0.2", "object-is@^1.1.2":
538 | "integrity" "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw=="
539 | "resolved" "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz"
540 | "version" "1.1.5"
541 | dependencies:
542 | "call-bind" "^1.0.2"
543 | "define-properties" "^1.1.3"
544 |
545 | "object-keys@^1.0.12", "object-keys@^1.1.1":
546 | "integrity" "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
547 | "resolved" "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
548 | "version" "1.1.1"
549 |
550 | "object.assign@^4.1.0", "object.assign@^4.1.2":
551 | "integrity" "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ=="
552 | "resolved" "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz"
553 | "version" "4.1.2"
554 | dependencies:
555 | "call-bind" "^1.0.0"
556 | "define-properties" "^1.1.3"
557 | "has-symbols" "^1.0.1"
558 | "object-keys" "^1.1.1"
559 |
560 | "object.entries@^1.1.1", "object.entries@^1.1.2":
561 | "integrity" "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g=="
562 | "resolved" "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz"
563 | "version" "1.1.5"
564 | dependencies:
565 | "call-bind" "^1.0.2"
566 | "define-properties" "^1.1.3"
567 | "es-abstract" "^1.19.1"
568 |
569 | "object.fromentries@^2.0.3":
570 | "integrity" "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw=="
571 | "resolved" "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz"
572 | "version" "2.0.5"
573 | dependencies:
574 | "call-bind" "^1.0.2"
575 | "define-properties" "^1.1.3"
576 | "es-abstract" "^1.19.1"
577 |
578 | "object.values@^1.1.1", "object.values@^1.1.2":
579 | "integrity" "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg=="
580 | "resolved" "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz"
581 | "version" "1.1.5"
582 | dependencies:
583 | "call-bind" "^1.0.2"
584 | "define-properties" "^1.1.3"
585 | "es-abstract" "^1.19.1"
586 |
587 | "parse5-htmlparser2-tree-adapter@^6.0.1":
588 | "integrity" "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA=="
589 | "resolved" "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz"
590 | "version" "6.0.1"
591 | dependencies:
592 | "parse5" "^6.0.1"
593 |
594 | "parse5@^6.0.1":
595 | "integrity" "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
596 | "resolved" "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz"
597 | "version" "6.0.1"
598 |
599 | "performance-now@^2.1.0":
600 | "integrity" "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
601 | "resolved" "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz"
602 | "version" "2.1.0"
603 |
604 | "prop-types-exact@^1.2.0":
605 | "integrity" "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA=="
606 | "resolved" "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz"
607 | "version" "1.2.0"
608 | dependencies:
609 | "has" "^1.0.3"
610 | "object.assign" "^4.1.0"
611 | "reflect.ownkeys" "^0.2.0"
612 |
613 | "prop-types@^15.6.2", "prop-types@^15.7.2":
614 | "integrity" "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="
615 | "resolved" "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
616 | "version" "15.8.1"
617 | dependencies:
618 | "loose-envify" "^1.4.0"
619 | "object-assign" "^4.1.1"
620 | "react-is" "^16.13.1"
621 |
622 | "raf@^3.4.1":
623 | "integrity" "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA=="
624 | "resolved" "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz"
625 | "version" "3.4.1"
626 | dependencies:
627 | "performance-now" "^2.1.0"
628 |
629 | "railroad-diagrams@^1.0.0":
630 | "integrity" "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234="
631 | "resolved" "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz"
632 | "version" "1.0.0"
633 |
634 | "randexp@0.4.6":
635 | "integrity" "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ=="
636 | "resolved" "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz"
637 | "version" "0.4.6"
638 | dependencies:
639 | "discontinuous-range" "1.0.0"
640 | "ret" "~0.1.10"
641 |
642 | "react-dom@^16.0.0-0", "react-dom@^16.14.0":
643 | "integrity" "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw=="
644 | "resolved" "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz"
645 | "version" "16.14.0"
646 | dependencies:
647 | "loose-envify" "^1.1.0"
648 | "object-assign" "^4.1.1"
649 | "prop-types" "^15.6.2"
650 | "scheduler" "^0.19.1"
651 |
652 | "react-is@^16.13.1", "react-is@^16.8.6":
653 | "integrity" "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
654 | "resolved" "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
655 | "version" "16.13.1"
656 |
657 | "react-test-renderer@^16.0.0-0":
658 | "integrity" "sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg=="
659 | "resolved" "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.14.0.tgz"
660 | "version" "16.14.0"
661 | dependencies:
662 | "object-assign" "^4.1.1"
663 | "prop-types" "^15.6.2"
664 | "react-is" "^16.8.6"
665 | "scheduler" "^0.19.1"
666 |
667 | "react@^0.14 || ^15.0.0 || ^16.0.0-alpha", "react@^16.0.0-0", "react@^16.14.0", "react@0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0":
668 | "integrity" "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g=="
669 | "resolved" "https://registry.npmjs.org/react/-/react-16.14.0.tgz"
670 | "version" "16.14.0"
671 | dependencies:
672 | "loose-envify" "^1.1.0"
673 | "object-assign" "^4.1.1"
674 | "prop-types" "^15.6.2"
675 |
676 | "reflect.ownkeys@^0.2.0":
677 | "integrity" "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA="
678 | "resolved" "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz"
679 | "version" "0.2.0"
680 |
681 | "ret@~0.1.10":
682 | "integrity" "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
683 | "resolved" "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz"
684 | "version" "0.1.15"
685 |
686 | "rst-selector-parser@^2.2.3":
687 | "integrity" "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE="
688 | "resolved" "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz"
689 | "version" "2.2.3"
690 | dependencies:
691 | "lodash.flattendeep" "^4.4.0"
692 | "nearley" "^2.7.10"
693 |
694 | "scheduler@^0.19.1":
695 | "integrity" "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA=="
696 | "resolved" "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz"
697 | "version" "0.19.1"
698 | dependencies:
699 | "loose-envify" "^1.1.0"
700 | "object-assign" "^4.1.1"
701 |
702 | "semver@^5.7.0", "semver@^5.7.1":
703 | "integrity" "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
704 | "resolved" "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz"
705 | "version" "5.7.1"
706 |
707 | "side-channel@^1.0.4":
708 | "integrity" "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw=="
709 | "resolved" "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz"
710 | "version" "1.0.4"
711 | dependencies:
712 | "call-bind" "^1.0.0"
713 | "get-intrinsic" "^1.0.2"
714 | "object-inspect" "^1.9.0"
715 |
716 | "string.prototype.trim@^1.2.1":
717 | "integrity" "sha512-Lnh17webJVsD6ECeovpVN17RlAKjmz4rF9S+8Y45CkMc/ufVpTkU3vZIyIC7sllQ1FCvObZnnCdNs/HXTUOTlg=="
718 | "resolved" "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz"
719 | "version" "1.2.5"
720 | dependencies:
721 | "call-bind" "^1.0.2"
722 | "define-properties" "^1.1.3"
723 | "es-abstract" "^1.19.1"
724 |
725 | "string.prototype.trimend@^1.0.4":
726 | "integrity" "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A=="
727 | "resolved" "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz"
728 | "version" "1.0.4"
729 | dependencies:
730 | "call-bind" "^1.0.2"
731 | "define-properties" "^1.1.3"
732 |
733 | "string.prototype.trimstart@^1.0.4":
734 | "integrity" "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw=="
735 | "resolved" "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz"
736 | "version" "1.0.4"
737 | dependencies:
738 | "call-bind" "^1.0.2"
739 | "define-properties" "^1.1.3"
740 |
741 | "tslib@^2.2.0":
742 | "integrity" "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
743 | "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz"
744 | "version" "2.3.1"
745 |
746 | "unbox-primitive@^1.0.1":
747 | "integrity" "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw=="
748 | "resolved" "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz"
749 | "version" "1.0.1"
750 | dependencies:
751 | "function-bind" "^1.1.1"
752 | "has-bigints" "^1.0.1"
753 | "has-symbols" "^1.0.2"
754 | "which-boxed-primitive" "^1.0.2"
755 |
756 | "which-boxed-primitive@^1.0.2":
757 | "integrity" "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg=="
758 | "resolved" "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz"
759 | "version" "1.0.2"
760 | dependencies:
761 | "is-bigint" "^1.0.1"
762 | "is-boolean-object" "^1.1.0"
763 | "is-number-object" "^1.0.4"
764 | "is-string" "^1.0.5"
765 | "is-symbol" "^1.0.3"
766 |
--------------------------------------------------------------------------------
/packages/shared/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | node: 'current',
8 | },
9 | },
10 | ],
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/packages/shared/jest.config.js:
--------------------------------------------------------------------------------
1 | /* -----------------------------------
2 | *
3 | * Jest
4 | *
5 | * -------------------------------- */
6 |
7 | module.exports = {
8 | testEnvironment: 'jsdom',
9 | globals: { __DEV__: true },
10 | roots: [''],
11 | collectCoverage: true,
12 | collectCoverageFrom: ['/src/**/*.{ts,tsx}'],
13 | coverageDirectory: '/tests/coverage',
14 | coveragePathIgnorePatterns: ['/node_modules/', '(.*).d.ts'],
15 | coverageThreshold: {
16 | global: {
17 | statements: 84,
18 | branches: 73,
19 | functions: 80,
20 | lines: 82,
21 | },
22 | },
23 | transform: {
24 | '^.+\\.tsx?$': 'ts-jest',
25 | '^.+\\js$': 'babel-jest',
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/packages/shared/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@component-elements/shared",
3 | "version": "1.0.0",
4 | "main": "./dist/index.js",
5 | "types": "./dist/index.d.ts",
6 | "license": "MIT",
7 | "directories": {
8 | "lib": "dist",
9 | "test": "tests"
10 | },
11 | "scripts": {
12 | "build": "tsc",
13 | "watch": "tsc -w",
14 | "lint": "eslint",
15 | "test": "jest"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/shared/src/element.ts:
--------------------------------------------------------------------------------
1 | import { getAttributeProps } from './parse';
2 | import { IComponent, CustomElement } from './model';
3 |
4 | /* -----------------------------------
5 | *
6 | * Async
7 | *
8 | * -------------------------------- */
9 |
10 | function getAsyncComponent(component: Promise, tagName: string): Promise {
11 | return component.then((response) => getComponentResult(response, tagName));
12 | }
13 |
14 | /* -----------------------------------
15 | *
16 | * Result
17 | *
18 | * -------------------------------- */
19 |
20 | function getComponentResult(response: IComponent, tagName: string) {
21 | let result = void 0;
22 |
23 | if (typeof response === 'function') {
24 | return response;
25 | }
26 |
27 | if (typeof response === 'object') {
28 | result = response[getNameFromTag(tagName)] || void 0;
29 | }
30 |
31 | return result;
32 | }
33 |
34 | /* -----------------------------------
35 | *
36 | * Element
37 | *
38 | * -------------------------------- */
39 |
40 | function getElementTag(tagName: string) {
41 | let result = tagName.toLowerCase();
42 |
43 | if (result.indexOf('-') < 0) {
44 | result = 'component-' + result;
45 | }
46 |
47 | return result;
48 | }
49 |
50 | /* -----------------------------------
51 | *
52 | * Tag
53 | *
54 | * -------------------------------- */
55 |
56 | function getNameFromTag(value: string) {
57 | value = value.toLowerCase();
58 |
59 | return value.replace(/(^\w|-\w)/g, (item) => item.replace(/-/, '').toUpperCase());
60 | }
61 |
62 | /* -----------------------------------
63 | *
64 | * Attributes
65 | *
66 | * -------------------------------- */
67 |
68 | function getElementAttributes(this: CustomElement) {
69 | const { attributes = [] } = this.__options;
70 | const result = {};
71 |
72 | if (!this.hasAttributes()) {
73 | return result;
74 | }
75 |
76 | return getAttributeProps(this.attributes, attributes);
77 | }
78 |
79 | /* -----------------------------------
80 | *
81 | * Export
82 | *
83 | * -------------------------------- */
84 |
85 | export { getElementTag, getElementAttributes, getAsyncComponent };
86 |
--------------------------------------------------------------------------------
/packages/shared/src/index.ts:
--------------------------------------------------------------------------------
1 | /* -----------------------------------
2 | *
3 | * Export
4 | *
5 | * -------------------------------- */
6 |
7 | export * from './element';
8 | export * from './parse';
9 | export * from './model';
10 |
--------------------------------------------------------------------------------
/packages/shared/src/model.ts:
--------------------------------------------------------------------------------
1 | /* -----------------------------------
2 | *
3 | * Component
4 | *
5 | * -------------------------------- */
6 |
7 | type IComponent = any;
8 |
9 | /* -----------------------------------
10 | *
11 | * Options
12 | *
13 | * -------------------------------- */
14 |
15 | interface IOptions {
16 | attributes?: string[];
17 | formatProps?: (props: any) => F;
18 | wrapComponent?: (child: any) => W;
19 | }
20 |
21 | /* -----------------------------------
22 | *
23 | * Errors
24 | *
25 | * -------------------------------- */
26 |
27 | enum ErrorTypes {
28 | Promise = 'Error: Promises cannot be used for SSR',
29 | Missing = 'Error: Cannot find component in provided function',
30 | Json = 'Error: Invalid JSON string passed to component',
31 | }
32 |
33 | /* -----------------------------------
34 | *
35 | * Element
36 | *
37 | * -------------------------------- */
38 |
39 | interface CustomElement extends HTMLElement {
40 | __mounted: boolean;
41 | __component: C;
42 | __properties?: IProps;
43 | __slots?: { [index: string]: any };
44 | __instance?: I;
45 | __children?: any;
46 | __options: IOptions;
47 | }
48 |
49 | /* -----------------------------------
50 | *
51 | * IProps
52 | *
53 | * -------------------------------- */
54 |
55 | interface IProps {
56 | [index: string]: any;
57 | }
58 |
59 | /* -----------------------------------
60 | *
61 | * Guards
62 | *
63 | * -------------------------------- */
64 |
65 | const isPromise = (input: any): input is Promise => {
66 | return input && typeof input.then === 'function';
67 | };
68 |
69 | /* -----------------------------------
70 | *
71 | * Self Closing
72 | *
73 | * -------------------------------- */
74 |
75 | const selfClosingTags = [
76 | 'area',
77 | 'base',
78 | 'br',
79 | 'col',
80 | 'hr',
81 | 'img',
82 | 'input',
83 | 'link',
84 | 'meta',
85 | 'source',
86 | 'embed',
87 | 'param',
88 | 'track',
89 | 'wbr',
90 | ];
91 |
92 | /* -----------------------------------
93 | *
94 | * Export
95 | *
96 | * -------------------------------- */
97 |
98 | export { IComponent, IOptions, IProps, ErrorTypes, CustomElement, isPromise, selfClosingTags };
99 |
--------------------------------------------------------------------------------
/packages/shared/src/parse.ts:
--------------------------------------------------------------------------------
1 | import { IProps, CustomElement, ErrorTypes } from './model';
2 |
3 | /* -----------------------------------
4 | *
5 | * parseJson
6 | *
7 | * -------------------------------- */
8 |
9 | function parseJson(this: CustomElement, value: string) {
10 | const { tagName } = this;
11 | const { formatProps } = this.__options;
12 |
13 | let result = {};
14 |
15 | try {
16 | result = JSON.parse(value);
17 | } catch {
18 | console.error(ErrorTypes.Json, `: <${tagName.toLowerCase()}>`);
19 | }
20 |
21 | if (formatProps) {
22 | result = formatProps(result);
23 | }
24 |
25 | return result;
26 | }
27 |
28 | /* -----------------------------------
29 | *
30 | * getDocument
31 | *
32 | * -------------------------------- */
33 |
34 | function getDocument(html: string) {
35 | const value = `\n${html}`;
36 |
37 | let nodes: Document;
38 |
39 | try {
40 | nodes = new DOMParser().parseFromString(value, 'text/html');
41 | } catch {
42 | // no-op
43 | }
44 |
45 | if (!nodes) {
46 | return void 0;
47 | }
48 |
49 | return nodes.body;
50 | }
51 |
52 | /* -----------------------------------
53 | *
54 | * getAttributeObject
55 | *
56 | * -------------------------------- */
57 |
58 | function getAttributeObject(attributes: NamedNodeMap): IProps {
59 | const result = {};
60 |
61 | if (!attributes?.length) {
62 | return result;
63 | }
64 |
65 | for (let i = attributes.length - 1; i >= 0; i--) {
66 | const item = attributes[i];
67 |
68 | result[item.name] = item.value;
69 | }
70 |
71 | return result;
72 | }
73 |
74 | /* -----------------------------------
75 | *
76 | * getAttributeProps
77 | *
78 | * -------------------------------- */
79 |
80 | function getAttributeProps(attributes: NamedNodeMap, allowed?: string[]): IProps {
81 | const values = getAttributeObject(attributes);
82 |
83 | let result = {};
84 |
85 | for (const key of Object.keys(values)) {
86 | if (allowed?.indexOf(key) === -1) {
87 | continue;
88 | }
89 |
90 | result[getPropKey(key)] = values[key];
91 | }
92 |
93 | return result;
94 | }
95 |
96 | /* -----------------------------------
97 | *
98 | * Attribute
99 | *
100 | * -------------------------------- */
101 |
102 | function getPropKey(value: string) {
103 | const sanitised = value.trim().replace(/[\s_]/g, '-');
104 |
105 | return (
106 | sanitised.charAt(0).toLowerCase() +
107 | sanitised.slice(1).replace(/-([a-z])/g, ({ 1: value }) => value.toUpperCase())
108 | );
109 | }
110 |
111 | /* -----------------------------------
112 | *
113 | * Export
114 | *
115 | * -------------------------------- */
116 |
117 | export { parseJson, getDocument, getPropKey, getAttributeObject, getAttributeProps };
118 |
--------------------------------------------------------------------------------
/packages/shared/tests/element.spec.ts:
--------------------------------------------------------------------------------
1 | import { getAsyncComponent, getElementTag, getElementAttributes } from '../src/element';
2 |
3 | /* -----------------------------------
4 | *
5 | * Variables
6 | *
7 | * -------------------------------- */
8 |
9 | const testComponent = () => void 0;
10 | const testObjectKey = 'TagName';
11 | const testValidTag = 'tag-name';
12 | const testInvalidTag = 'tag';
13 | const testAttributeKey = 'testTitle';
14 | const testProps = { [testAttributeKey]: 'testTitle' };
15 | const testAttribute = { name: testAttributeKey, value: 'testTitle' };
16 |
17 | /* -----------------------------------
18 | *
19 | * Element
20 | *
21 | * -------------------------------- */
22 |
23 | describe('element', () => {
24 | afterAll(() => jest.clearAllMocks());
25 |
26 | describe('getAsyncComponent()', () => {
27 | it('resolves a Promise that returns a component function', async () => {
28 | const result = await getAsyncComponent(Promise.resolve(testComponent), testValidTag);
29 |
30 | expect(result).toEqual(testComponent);
31 | });
32 |
33 | it('resolves a Promise that returns an object with component', async () => {
34 | const result = await getAsyncComponent(
35 | Promise.resolve({ [testObjectKey]: testComponent }),
36 | testValidTag
37 | );
38 |
39 | expect(result).toEqual(testComponent);
40 | });
41 |
42 | it('returns undefined if object does not contain matching component', async () => {
43 | const result = await getAsyncComponent(
44 | Promise.resolve({ WrongKey: testComponent }),
45 | testValidTag
46 | );
47 |
48 | expect(result).toEqual(void 0);
49 | });
50 | });
51 |
52 | describe('getElementTag()', () => {
53 | it('accepts a non valid custom element tag and modifies', () => {
54 | const result = getElementTag(testInvalidTag);
55 |
56 | expect(result).toEqual(`component-${testInvalidTag}`);
57 | });
58 |
59 | it('accepts a valid custom element tag and returns', () => {
60 | const result = getElementTag(testValidTag);
61 |
62 | expect(result).toEqual(testValidTag);
63 | });
64 | });
65 |
66 | describe('getElementAttributes()', () => {
67 | const element = {
68 | __options: { attributes: [testAttributeKey] },
69 | attributes: [testAttribute],
70 | hasAttributes: () => true,
71 | };
72 |
73 | it('converts defined attributes into props object', () => {
74 | const result = getElementAttributes.call(element);
75 |
76 | expect(result).toEqual(testProps);
77 | });
78 |
79 | it('ignores attributes that are not defined via attributes option', () => {
80 | const result = getElementAttributes.call({ ...element, __options: [] });
81 |
82 | expect(result).toEqual({});
83 | });
84 |
85 | it('skips conversion if element has no attributes', () => {
86 | const result = getElementAttributes.call({ ...element, hasAttributes: () => false });
87 |
88 | expect(result).toEqual({});
89 | });
90 | });
91 | });
92 |
--------------------------------------------------------------------------------
/packages/shared/tests/parse.spec.ts:
--------------------------------------------------------------------------------
1 | import { h } from 'preact';
2 | import { mount } from 'enzyme';
3 | import { parseJson, getPropKey } from '../src/parse';
4 |
5 | /* -----------------------------------
6 | *
7 | * Variables
8 | *
9 | * -------------------------------- */
10 |
11 | const testHeading = 'testHeading';
12 | const testData = { testHeading };
13 | const testJson = JSON.stringify(testData);
14 |
15 | /* -----------------------------------
16 | *
17 | * Parse
18 | *
19 | * -------------------------------- */
20 |
21 | describe('parse', () => {
22 | describe('parseJson()', () => {
23 | const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
24 |
25 | const properties = {
26 | tagName: 'tag-name',
27 | __options: {},
28 | };
29 |
30 | afterAll(() => errorSpy.mockClear());
31 |
32 | it('should correctly parse json', () => {
33 | const result = parseJson.call(properties, testJson);
34 |
35 | expect(result).toEqual(testData);
36 | });
37 |
38 | it('should handle invalid json', () => {
39 | const result = parseJson.call(properties, '{test:}');
40 |
41 | expect(result).toEqual({});
42 | expect(errorSpy).toHaveBeenCalled();
43 | });
44 |
45 | it('should run "formatProps" if defined via options', () => {
46 | const formatProps = (props: any) => ({ ...props, format: true });
47 | const testProps = { ...properties, __options: { ...properties.__options, formatProps } };
48 | const result = parseJson.call(testProps, testJson);
49 |
50 | expect(result.hasOwnProperty('format')).toBe(true);
51 | expect(result.format).toEqual(true);
52 | });
53 | });
54 |
55 | describe('getPropKey', () => {
56 | const testCamel = 'testSlot';
57 | const testKebab = 'test-slot';
58 | const testSnake = 'test_slot';
59 | const testPascal = 'TestSlot';
60 | const testSentence = 'Test slot';
61 |
62 | it('should normalise casing from kebab to camel', () => {
63 | const result = getPropKey(testKebab);
64 |
65 | expect(result).toEqual(testCamel);
66 | });
67 |
68 | it('should normalise casing from snake to camel', () => {
69 | const result = getPropKey(testSnake);
70 |
71 | expect(result).toEqual(testCamel);
72 | });
73 |
74 | it('should normalise casing from plascal to camel', () => {
75 | const result = getPropKey(testPascal);
76 |
77 | expect(result).toEqual(testCamel);
78 | });
79 |
80 | it('should normalise casing from sentence to camel', () => {
81 | const result = getPropKey(testSentence);
82 |
83 | expect(result).toEqual(testCamel);
84 | });
85 | });
86 | });
87 |
--------------------------------------------------------------------------------
/packages/shared/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./dist",
4 | "module": "esnext",
5 | "target": "esnext",
6 | "declaration": true,
7 | "moduleResolution": "node",
8 | "allowSyntheticDefaultImports": true
9 | },
10 | "include": ["src"],
11 | "exclude": ["node_modules", "dist"]
12 | }
13 |
--------------------------------------------------------------------------------
/packages/shared/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "noImplicitAny": false,
6 | "removeComments": true,
7 | "noLib": false,
8 | "noEmitOnError": true,
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "esModuleInterop": true,
12 | "moduleResolution": "node",
13 | "target": "ES2018",
14 | "sourceMap": true,
15 | "lib": ["DOM", "ES2015", "ES2016", "ES2017", "ES2018", "ES2019"]
16 | },
17 | "exclude": ["node_modules", "webpack.config.ts", "tests", "./dist"]
18 | }
19 |
--------------------------------------------------------------------------------