feat(dev-server-hmr): add web component HMR plugin

This commit is contained in:
Lars den Bakker
2020-11-14 19:05:06 +01:00
parent 0fb0bd304f
commit c9dc557f63
65 changed files with 2809 additions and 79 deletions

View File

@@ -0,0 +1,5 @@
---
'@open-wc/dev-server-wc-hmr': patch
---
first release

View File

@@ -6,3 +6,4 @@ stats.html
/packages/**/test-node/**/snapshots /packages/**/test-node/**/snapshots
/packages/demoing-storybook/storybook-static/**/* /packages/demoing-storybook/storybook-static/**/*
/packages/**/demo/**/* /packages/**/demo/**/*
/packages/dev-server-hmr/src/patches/**/*

View File

@@ -2,6 +2,8 @@ module.exports = {
extends: ['./packages/eslint-config/index.js', require.resolve('eslint-config-prettier')], extends: ['./packages/eslint-config/index.js', require.resolve('eslint-config-prettier')],
rules: { rules: {
'lit/no-useless-template-literals': 'off', 'lit/no-useless-template-literals': 'off',
'consistent-return': 'off',
'max-classes-per-file': 'off',
}, },
overrides: [ overrides: [
{ {

View File

@@ -0,0 +1,240 @@
# Development >> Hot Module Replacement ||50
> This project is currently experimental. Try it out and let us know what you think!
Plugin for "hot module replacement" or "fast refresh" with web components.
Keeps track of web component definitions in your code, and updates them at runtime on change. This is faster than a full page reload and preserves the page's state.
HMR requires the web component base class to implement a `hotReplaceCallback`.
## Installation
Install the package:
```
npm i --save-dev @web/dev-server-hmr
```
Add the plugin to your `web-dev-server-config.mjs`:
```js
import { hmrPlugin } from '@open-wc/dev-server-hmr';
export default {
plugins: [
hmrPlugin({
include: ['src/**/*'],
}),
],
};
```
## Implementations
### Vanilla
For vanilla web component projects that don't implement any base class or library this plugin should detect your components correctly. Read more below on how to implement the `hotReplaceCallback`.
### LitElement
LitElement v2 supports HMR with a small code patch included in the preset. There is no support yet for the v3 prerelease.
```js
import { hmrPlugin, presets } from '@open-wc/dev-server-hmr';
export default {
plugins: [
hmrPlugin({
include: ['src/**/*'],
presets: [presets.litElement],
}),
],
};
```
### FAST Element
We have experimental support for FAST element using a small code patch included in the preset. This might not cover all use cases yet, let us know if you run into any issues!
```js
import { hmrPlugin, presets } from '@open-wc/dev-server-hmr';
export default {
plugins: [
hmrPlugin({
include: ['src/**/*'],
presets: [presets.fastElement],
}),
],
};
```
### Other libraries
If you know any other libraries that work correctly with HMR we can add presets for them here. Presets help by configuring the detection of base classes, decorators, and/or runtime code patches.
## Detecting web components
To "hot replace" an edited web component we have to be able to detect component definitions in your code. By default we look for usage of `customElements.define` and extending from `HTMLElement`.
For other use cases, you can specify base classes or decorators to indicate component definitions. We have some presets shown above which configure this for libraries that we know to work with HMR.
### Base classes
The base class option detects web components that extend a specific base class. The base class can be matched by name, or as an import from a specific module.
```js
hmrPlugin({
include: ['src/**/*'],
baseClasses: [
// anything that extends a class called MyElement
{ name: 'MyElement' },
// anything that extends a class called MyElement imported from 'my-element'
{ name: 'MyElement', import: 'my-element' },
// anything that extends a class called MyElement imported from './src/MyElement.js' (relative to current working directory)
{ name: 'MyElement', import: './src/MyElement.js' },
// anything that extends a default importeded class from 'my-element'
{ name: 'default', import: 'my-element' },
],
});
```
### Decorator
The plugin can also detect web components defined using decorators. The decorators can be matched by name, or as an import from a specific module.
```js
hmrPlugin({
include: ['src/**/*'],
decorators: [
// any class that uses a decorator called customElement
{ name: 'customElement' },
// any class that uses a decorator called customElement imported from 'my-element'
{ name: 'customElement', import: 'my-element' },
// any class that uses a decorator called customElement imported from './src/MyElement.js' (relative to current working directory)
{ name: 'customElement', import: './src/MyElement.js' },
// any class that uses a decorator default imported from 'my-element'
{ name: 'default', import: 'my-element' },
],
});
```
### Functions
We don't currently support function based web components. Let us know if you have ideas on how this could work!
## Limitations
HMR workflows are not perfect. We're overwriting and moving around code at runtime. It breaks assumptions you normally make about your code. We recommended periodically doing a full refresh of the page, especially when you encounter strange behavior.
The following limitations should be kept in mind when working with open-wc HMR:
- Modules containing web components are re-imported under a new name and only the web component class is replaced. Side effects are triggered again but exported symbols are not updated.
- Constructors for already created elements are not re-run when a class is replaced. Otherwise it would reset the properties of your element. This does mean that newly added properties don't show up.
- Instance class fields act like properties defined in a constructor, and newly added or changed class fields are not hot replaced.
- Newly created elements do use the new constructors and class fields.
> Did you run into other limitations? Let us know so we can improve this list.
## Implementing HMR
When hot replacing a web component class we can't replace the actual class. The custom element registry doesn't allow re-registration and we want to preserve the state of already rendered components. Instead, we patch the initial class with the properties from the updates class.
This updating logic can be different for each base class, and it can be implemented using the `hotReplaceCallback`.
This is the default implementation:
```js
function updateObjectMembers(currentObj, newObj) {
const currentProperties = new Set(Object.getOwnPropertyNames(hmrClass));
const newProperties = new Set(Object.getOwnPropertyNames(newClass));
// add new and overwrite existing properties/methods
for (const prop of Object.getOwnPropertyNames(newClass)) {
const descriptor = Object.getOwnPropertyDescriptor(newClass, prop);
if (descriptor && descriptor.configurable) {
Object.defineProperty(hmrClass, prop, descriptor);
}
}
// delete removed properties
for (const existingProp of currentProperties) {
if (!newProperties.has(existingProp)) {
try {
delete hmrClass[existingProp];
} catch {}
}
}
}
class MyElement extends HTMLElement {
// static callback, called once when a class updates
static hotReplaceCallback(newClass) {
updateObjectMembers(this, newClass);
updateObjectMembers(this.prototype, newClass.prototype);
}
// instance callback, called for each connected element
hotReplaceCallback() {
// this should kick off re-rendering
this.update();
}
}
```
### Static callback
The static `hotReplaceCallback` callback is called once for each replacement on the initial class of the component. This is where you can copy over properties from the new class to the existing class.
Implementing this callback is not mandatory, by default we copy over properties of the new class to the existing class. If this is not sufficient, you can customize this logic.
### Instance callback
The instance callback is called on each connected element implementing the replaced class. Implementing this is necessary to do some work at the instance level, such as trigger a re-render or style update.
When the instance callback is called, all the class members (properties, methods, etc.) have already been updated. So it could be as simple as kicking off the regular updating/rendering pipeline.
### Patching
If you don't want to include the HMR code in your production code, you could patch in the callbacks externally:
```js
import { MyElement } from 'my-element';
MyElement.hotReplaceCallback = function hotReplaceCallback(newClass) {
// code for the static callback
};
MyElement.prototype.hotReplaceCallback = function hotReplaceCallback(newClass) {
// code for the instance callback
};
```
Make sure this code is loaded before any of your components are loaded. You could also do this using the `patch` option in the config:
```js
import { hmrPlugin } from '@open-wc/dev-server-hmr';
const myElementPatch = `
import { MyElement } from 'my-element';
MyElement.hotReplaceCallback = function hotReplaceCallback(newClass) {
// code for the static callback
};
MyElement.prototype.hotReplaceCallback = function hotReplaceCallback(newClass) {
// code for the instance callback
};
`;
export default {
plugins: [
hmrPlugin({
include: ['src/**/*'],
baseClasses: [{ name: 'MyElement', import: 'my-element' }],
patches: [myElementPatch],
}),
],
};
```

View File

@@ -44,7 +44,7 @@
"@types/eslint": "^7.2.4", "@types/eslint": "^7.2.4",
"@types/estree": "^0.0.45", "@types/estree": "^0.0.45",
"@types/parse5-htmlparser2-tree-adapter": "^5.0.1", "@types/parse5-htmlparser2-tree-adapter": "^5.0.1",
"@web/dev-server": "^0.0.19", "@web/dev-server": "^0.0.24",
"@web/test-runner": "^0.9.5", "@web/test-runner": "^0.9.5",
"@web/test-runner-playwright": "^0.6.4", "@web/test-runner-playwright": "^0.6.4",
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",

View File

@@ -0,0 +1,5 @@
# Demoing via storybook
Plugin for "hot module replacement" or "fast refresh" with web components.
See [our website](https://open-wc.org/docs/development/hot-module-replacement/) for full documentation or view the docs [on github](../../docs/docs/development/hot-module-replacement.md)

View File

@@ -0,0 +1,6 @@
<html>
<body>
<todo-app></todo-app>
<script type="module" src="./src/todo-app.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,15 @@
import { esbuildPlugin } from '@web/dev-server-esbuild';
import { hmrPlugin, presets } from '../../index.mjs';
export default {
open: 'packages/dev-server-hmr/demo/fast-element/',
rootDir: '../..',
nodeResolve: true,
plugins: [
esbuildPlugin({ ts: true }),
hmrPlugin({
exclude: ['**/*/node_modules/**/*'],
presets: [presets.fastElement],
}),
],
};

View File

@@ -0,0 +1,7 @@
import { css } from '@microsoft/fast-element';
export const sharedStyles = css`
.shared-template {
color: green;
}
`;

View File

@@ -0,0 +1,5 @@
import { html } from '@microsoft/fast-element';
export const sharedTemplate = html`
<p class="shared-template">Shared template</p>
`;

View File

@@ -0,0 +1,15 @@
import { FASTElement, customElement, html } from '@microsoft/fast-element';
import './todo-list.js';
const template = html<TodoApp>`
<h1>Todo app</h1>
<todo-list></todo-list>
`;
@customElement({
name: 'todo-app',
template,
})
class TodoApp extends FASTElement {
static definition = { name: 'todo-app', template };
}

View File

@@ -0,0 +1,50 @@
import { FASTElement, customElement, observable, html, css } from '@microsoft/fast-element';
const styles = css`
:host {
display: flex;
justify-content: space-between;
align-items: center;
}
.message {
color: blue;
}
.delete {
border: none;
width: auto;
background: transparent;
font-size: 12px;
padding: 6px 4px;
}
`;
const template = html<TodoItem>`
<div>
<input
id="checkbox"
type="checkbox"
:checked=${x => x.checked}
@change=${(x, c) => x._onCheckedChanged(c.event)}
/>
<label for="checkbox" class="message">${x => x.message}</label>
</div>
<button class="delete" aria-label="Delete" @click=${x => x._onDelete()}>❌</button>
`;
@customElement({ name: 'todo-item', template, styles })
class TodoItem extends FASTElement {
@observable checked = false;
@observable message = '';
_onCheckedChanged(e: Event) {
this.dispatchEvent(
new CustomEvent('checked-changed', { detail: (e.target as HTMLInputElement).checked }),
);
}
_onDelete() {
this.dispatchEvent(new Event('delete'));
}
}

View File

@@ -0,0 +1,94 @@
import { FASTElement, customElement, observable, repeat, html, css } from '@microsoft/fast-element';
import { sharedTemplate } from './sharedTemplate';
import { sharedStyles } from './sharedStyles';
import './todo-item.js';
interface TodoItem {
message: string;
checked: boolean;
}
const styles = [
sharedStyles,
css`
ul {
list-style: none;
padding-inline-start: 0;
max-width: 200px;
}
.add {
display: flex;
}
.add > * {
margin-right: 6px;
}
`,
];
const template = html<TodoList>`
<ul>
${repeat(
x => x.items,
html<TodoItem, TodoList>`
<li>
<todo-item
:message=${x => x.message}
:checked=${x => x.checked}
data-i=${(_, c) => c.index}
@checked-changed=${(_, c) => c.parent._onCheckedChanged(c.event as any)}
@delete=${(_, c) => c.parent._onDelete(c.event)}
></todo-item>
</li>
`,
)}
</ul>
<div class="add">
<input id="input" placeholder="Add a TODO" autocomplete="off" />
<button @click=${x => x._addTodo()}>
Add
</button>
</div>
${sharedTemplate}
`;
@customElement({ name: 'todo-list', template, styles })
class TodoList extends FASTElement {
@observable items: TodoItem[] = [
{ message: 'Do A', checked: true },
{ message: 'Do B', checked: false },
{ message: 'Do C', checked: true },
{ message: 'Do D', checked: false },
{ message: 'Do E', checked: false },
];
_onCheckedChanged(e: Event & { detail: boolean }) {
const { i: iString } = (e.target as HTMLElement).dataset;
const i = Number(iString);
const item = this.items[i];
const newItems = this.items.slice();
newItems.splice(i, 1, { ...item, checked: e.detail });
this.items = newItems;
}
_onDelete(e: Event) {
const { i: iString } = (e.target as HTMLElement).dataset;
const i = Number(iString);
const newItems = this.items.slice();
newItems.splice(i, 1);
this.items = newItems;
}
_addTodo() {
const input = this.shadowRoot!.getElementById('input') as HTMLInputElement;
const message = input.value;
if (!message) {
return;
}
input.value = '';
this.items = [...this.items, { message, checked: false }];
}
}

View File

@@ -0,0 +1,12 @@
{
"include": ["src"],
"compilerOptions": {
"target": "ESNEXT" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module":"ESNext",
"strict": true /* Enable all strict type-checking options. */,
/* Module Resolution Options */
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
"experimentalDecorators": true
}
}

View File

@@ -0,0 +1,6 @@
<html>
<body>
<todo-app></todo-app>
<script type="module" src="./src/todo-app.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,15 @@
import { esbuildPlugin } from '@web/dev-server-esbuild';
import { hmrPlugin, presets } from '../../index.mjs';
export default {
open: 'packages/dev-server-hmr/demo/lit-element-ts/',
rootDir: '../..',
nodeResolve: true,
plugins: [
esbuildPlugin({ ts: true }),
hmrPlugin({
exclude: ['**/*/node_modules/**/*'],
presets: [presets.litElement],
}),
],
};

View File

@@ -0,0 +1,7 @@
import { css } from 'lit-element';
export const sharedStyles = css`
.shared-template {
color: green;
}
`;

View File

@@ -0,0 +1,3 @@
import { html } from 'lit-element';
export const sharedTemplate = html`<p class="shared-template">Shared template</p>`;

View File

@@ -0,0 +1,12 @@
import { LitElement, customElement, html } from 'lit-element';
import './todo-list.js';
@customElement('todo-app')
class TodoApp extends LitElement {
render() {
return html`
<h1>Todo app</h1>
<todo-list></todo-list>
`;
}
}

View File

@@ -0,0 +1,55 @@
import { LitElement, property, customElement, html, css } from 'lit-element';
@customElement('todo-item')
class TodoItem extends LitElement {
static get styles() {
return css`
:host {
display: flex;
justify-content: space-between;
align-items: center;
}
.message {
color: blue;
}
.delete {
border: none;
width: auto;
background: transparent;
font-size: 12px;
padding: 6px 4px;
}
`;
}
@property({ type: Boolean })
checked = false;
@property({ type: String })
message?: string;
render() {
return html`
<div>
<input
id="checkbox"
type="checkbox"
.checked=${!!this.checked}
@change=${this._onCheckedChanged}
/>
<label for="checkbox" class="message">${this.message}</label>
</div>
<button class="delete" aria-label="Delete" @click=${this._onDelete}>❌</button>
`;
}
_onCheckedChanged(e: any) {
this.dispatchEvent(new CustomEvent('checked-changed', { detail: e.target.checked }));
}
_onDelete() {
this.dispatchEvent(new Event('delete'));
}
}

View File

@@ -0,0 +1,93 @@
import { LitElement, customElement, property, html, css } from 'lit-element';
import { sharedTemplate } from './sharedTemplate';
import { sharedStyles } from './sharedStyles';
import './todo-item.js';
@customElement('todo-list')
class TodoList extends LitElement {
static get styles() {
return [
sharedStyles,
css`
ul {
list-style: none;
padding-inline-start: 0;
max-width: 200px;
}
.add {
display: flex;
}
.add > * {
margin-right: 6px;
}
`,
];
}
@property({ type: Array })
items = [
{ message: 'Do A', checked: true },
{ message: 'Do B', checked: false },
{ message: 'Do C', checked: true },
{ message: 'Do D', checked: false },
{ message: 'Do E', checked: false },
];
render() {
return html`
<ul>
${this.items.map(
(item, i) =>
html`
<li>
<todo-item
.message=${item.message}
.checked=${item.checked}
data-i=${i}
@checked-changed=${this._onCheckedChanged}
@delete=${this._onDelete}
></todo-item>
</li>
`,
)}
</ul>
<div class="add">
<input id="input" placeholder="Add a TODO" autocomplete="off" /><button
@click=${this._addTodo}
>
Add
</button>
</div>
${sharedTemplate}
`;
}
_onCheckedChanged(e: any) {
const { i } = e.target.dataset;
const item = this.items[i];
const newItems = this.items.slice();
newItems.splice(i, 1, { ...item, checked: e.detail });
this.items = newItems;
}
_onDelete(e) {
const { i } = e.target.dataset;
const newItems = this.items.slice();
newItems.splice(i, 1);
this.items = newItems;
}
_addTodo() {
const input = this.shadowRoot!.getElementById('input') as HTMLInputElement;
const message = input.value;
if (!message) {
return;
}
input.value = '';
this.items = [...this.items, { message, checked: false }];
}
}

View File

@@ -0,0 +1,12 @@
{
"include": ["src"],
"compilerOptions": {
"target": "ESNEXT" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module":"ESNext",
"strict": true /* Enable all strict type-checking options. */,
/* Module Resolution Options */
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
"experimentalDecorators": true
}
}

View File

@@ -0,0 +1,6 @@
<html>
<body>
<todo-app></todo-app>
<script type="module" src="./src/todo-app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,13 @@
import { hmrPlugin, presets } from '../../index.mjs';
export default {
open: 'packages/dev-server-hmr/demo/lit-element/',
rootDir: '../..',
nodeResolve: true,
plugins: [
hmrPlugin({
exclude: ['**/*/node_modules/**/*'],
presets: [presets.litElement],
}),
],
};

View File

@@ -0,0 +1,7 @@
import { css } from 'lit-element';
export const sharedStyles = css`
.shared-template {
color: green;
}
`;

View File

@@ -0,0 +1,3 @@
import { html } from 'lit-element';
export const sharedTemplate = html`<p class="shared-template">Shared template</p>`;

View File

@@ -0,0 +1,13 @@
import { LitElement, html } from 'lit-element';
import './todo-list.js';
class TodoApp extends LitElement {
render() {
return html`
<h1>Todo app</h1>
<todo-list></todo-list>
`;
}
}
customElements.define('todo-app', TodoApp);

View File

@@ -0,0 +1,57 @@
import { LitElement, html, css } from 'lit-element';
class TodoItem extends LitElement {
static get styles() {
return css`
:host {
display: flex;
justify-content: space-between;
align-items: center;
}
.message {
color: blue;
}
.delete {
border: none;
width: auto;
background: transparent;
font-size: 12px;
padding: 6px 4px;
}
`;
}
static get properties() {
return {
checked: { type: Boolean },
message: { type: String },
};
}
render() {
return html`
<div>
<input
id="checkbox"
type="checkbox"
.checked=${!!this.checked}
@change=${this._onCheckedChanged}
/>
<label for="checkbox" class="message">${this.message}</label>
</div>
<button class="delete" aria-label="Delete" @click=${this._onDelete}>❌</button>
`;
}
_onCheckedChanged(e) {
this.dispatchEvent(new CustomEvent('checked-changed', { detail: e.target.checked }));
}
_onDelete() {
this.dispatchEvent(new Event('delete'));
}
}
customElements.define('todo-item', TodoItem);

View File

@@ -0,0 +1,100 @@
import { LitElement, html, css } from 'lit-element';
import { sharedTemplate } from './sharedTemplate.js';
import { sharedStyles } from './sharedStyles.js';
import './todo-item.js';
class TodoList extends LitElement {
static get styles() {
return [
sharedStyles,
css`
ul {
list-style: none;
padding-inline-start: 0;
max-width: 200px;
}
.add {
display: flex;
}
.add > * {
margin-right: 6px;
}
`,
];
}
static get properties() {
return { items: { type: Object } };
}
constructor() {
super();
this.items = [
{ message: 'Do A', checked: true },
{ message: 'Do B', checked: false },
{ message: 'Do C', checked: true },
{ message: 'Do D', checked: false },
{ message: 'Do E', checked: false },
];
}
render() {
return html`
<ul>
${this.items.map(
(item, i) =>
html`
<li>
<todo-item
.message=${item.message}
.checked=${item.checked}
data-i=${i}
@checked-changed=${this._onCheckedChanged}
@delete=${this._onDelete}
></todo-item>
</li>
`,
)}
</ul>
<div class="add">
<input id="input" placeholder="Add a TODO" autocomplete="off" /><button
@click=${this._addTodo}
>
Add
</button>
</div>
${sharedTemplate}
`;
}
_onCheckedChanged(e) {
const { i } = e.target.dataset;
const item = this.items[i];
const newItems = this.items.slice();
newItems.splice(i, 1, { ...item, checked: e.detail });
this.items = newItems;
}
_onDelete(e) {
const { i } = e.target.dataset;
const newItems = this.items.slice();
newItems.splice(i, 1);
this.items = newItems;
}
_addTodo() {
const input = this.shadowRoot.getElementById('input');
const message = input.value;
if (!message) {
return;
}
input.value = '';
this.items = [...this.items, { message, checked: false }];
}
}
customElements.define('todo-list', TodoList);

View File

@@ -0,0 +1,8 @@
<html>
<head></head>
<body>
<vanilla-app></vanilla-app>
<script type="module" src="./src/vanilla-app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,18 @@
import path from 'path';
import { fileURLToPath } from 'url';
import { hmrPlugin } from '../../index.mjs';
const dirname = path.dirname(fileURLToPath(import.meta.url));
export default {
open: 'demo/vanilla/',
nodeResolve: true,
plugins: [
hmrPlugin({
exclude: ['**/*/node_modules/**/*'],
baseClasses: [
{ name: 'SharedElement', import: path.resolve(dirname, './src/SharedElement.js') },
],
}),
],
};

View File

@@ -0,0 +1,24 @@
export class BaseClass extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
if (super.connectedCallback) {
super.connectedCallback();
}
this.update();
}
hotReplaceCallback() {
this.update();
}
update() {
this.shadowRoot.innerHTML = `
<style>${this.styles()}</style>
${this.render()}
`;
}
}

View File

@@ -0,0 +1,17 @@
import { BaseClass } from './BaseClass.js';
export class VanillaElementBase extends BaseClass {
styles() {
return `
.base {
color: blue;
}
`;
}
render() {
return `
<p class="base">Vanilla element base</p>
`;
}
}

View File

@@ -0,0 +1,5 @@
export const sharedStyles = `
.shared-template {
color: green;
}
`;

View File

@@ -0,0 +1 @@
export const sharedTemplate = '<p class="shared-template">Shared template</p>';

View File

@@ -0,0 +1,32 @@
import { BaseClass } from './BaseClass.js';
import { sharedTemplate } from './sharedTemplate.js';
import { sharedStyles } from './sharedStyles.js';
import './vanilla-message.js';
import './vanilla-element.js';
class VanillaApp extends BaseClass {
styles() {
return `
${sharedStyles}
h1 {
color: blue;
}
`;
}
render() {
return `
<h1>Vanilla app</h1>
${sharedTemplate}
<vanilla-message message="Hello"></vanilla-message>
<vanilla-message message="World"></vanilla-message>
<vanilla-message message="Goodbye"></vanilla-message>
<vanilla-element></vanilla-element>
`;
}
}
customElements.define('vanilla-app', VanillaApp);

View File

@@ -0,0 +1,22 @@
import { VanillaElementBase } from './VanillaElementBase.js';
class VanillaElement extends VanillaElementBase {
styles() {
return `
${super.styles()}
.element {
color: red;
}
`;
}
render() {
return `
${super.render()}
<p class="element">Vanilla element</p>
`;
}
}
customElements.define('vanilla-element', VanillaElement);

View File

@@ -0,0 +1,39 @@
import { BaseClass } from './BaseClass.js';
class VanillaMessage extends BaseClass {
static observedAttributes = ['message'];
constructor() {
super();
this.message = '';
}
attributeChangedCallback(name, oldValue, newValue) {
this[name] = newValue;
}
set message(value) {
this._message = value;
this.update();
}
get message() {
return this._message;
}
styles() {
return `
p {
color: blue;
}
`;
}
render() {
return `
<p>Message: ${this.message}</p>
`;
}
}
customElements.define('vanilla-message', VanillaMessage);

View File

@@ -0,0 +1,5 @@
import module from './src/index.js';
const { hmrPlugin, presets, WC_HMR_MODULE_RUNTIME } = module;
export { hmrPlugin, presets, WC_HMR_MODULE_RUNTIME };

View File

@@ -0,0 +1,63 @@
{
"name": "@open-wc/dev-server-wc-hmr",
"version": "0.0.0",
"publishConfig": {
"access": "public"
},
"description": "Plugin for HMR with web components",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/open-wc/open-wc.git",
"directory": "packages/dev-server-wc-hmr"
},
"author": "open-wc",
"homepage": "https://github.com/open-wc/open-wc/packages/dev-server-wc-hmr",
"main": "dist/index.js",
"scripts": {
"start:fast": "wds --config demo/fast-element/server.config.mjs",
"start:lit": "wds --config demo/lit-element/server.config.mjs",
"start:lit-ts": "wds --config demo/lit-element-ts/server.config.mjs",
"start:vanilla": "wds --config demo/vanilla/server.config.mjs",
"test": "npm run test:node",
"test:node": "mocha test-node --recursive",
"test:watch": "npm run test:node -- --watch --watchfiles test"
},
"files": [
"dist",
"src"
],
"keywords": [
"web",
"dev server",
"hmr",
"hot",
"module",
"replacement",
"reload",
"web",
"components"
],
"dependencies": {
"@babel/core": "^7.12.3",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-syntax-import-assertions": "^7.12.1",
"@babel/plugin-syntax-top-level-await": "^7.12.1",
"@web/dev-server-core": "^0.2.18",
"@web/dev-server-hmr": "^0.1.6",
"picomatch": "^2.2.2"
},
"devDependencies": {
"@babel/core": "^7.12.3",
"@microsoft/fast-element": "^0.19.1",
"@types/babel__core": "^7.1.12",
"@types/babel__generator": "^7.6.2",
"@types/babel__parser": "^7.1.1",
"@types/babel__traverse": "^7.0.15",
"@types/picomatch": "^2.2.1",
"@web/dev-server": "^0.0.24",
"@web/dev-server-esbuild": "^0.2.8",
"lit-element": "^2.4.0",
"mocha": "^8.2.1"
}
}

View File

@@ -0,0 +1,191 @@
/** @typedef {import('@babel/core').PluginObj} PluginObj */
/** @template T @typedef {import('@babel/core').NodePath<T>} NodePath<T> */
/**
* @typedef {object} BaseClass
* @property {string} name
* @property {string} [import]
*/
/**
* @typedef {object} Decorator
* @property {string} name
* @property {string} [import]
*/
/**
* @typedef {object} BabelPluginWcHmrOptions
* @property {string} rootDir
* @property {BaseClass[]} [baseClasses]
* @property {Decorator[]} [decorators]
* @property {string[]} [patches]
*/
const { types } = require('@babel/core');
const path = require('path');
const { findDefinedCustomElement } = require('./customElementsDefine');
const { findDecoratedCustomElement } = require('./decorators');
const { injectRegisterClass, injectRuntime } = require('./inject');
const { parseOptions, singlePath, addToSet } = require('./utils');
const { getImportedVariableNames } = require('./getImportedVariableNames');
const { implementsBaseClass } = require('./class');
const { createError } = require('../utils');
/** @returns {PluginObj} */
function babelPluginWcHmr() {
return {
visitor: {
Program(program) {
if (!this.filename) throw createError('Missing filename');
const resolvedFilename = path.resolve(this.filename);
const opts = parseOptions(/** @type {BabelPluginWcHmrOptions} */ (this.opts));
const baseClasses = opts.baseClasses || [];
const decorators = opts.decorators || [];
/** @type {Set<string>} */
const baseClassNames = new Set();
/** @type {Set<string>} */
const decoratorNames = new Set();
/** @type {Set<string>} */
const injectedClassNames = new Set();
let injectedRegister = false;
/**
* @param {NodePath<any>} nodePath
* @param {string} name
* @param {boolean} insertAfter
*/
function maybeInjectRegister(nodePath, name, insertAfter = false) {
if (injectedClassNames.has(name)) {
return;
}
injectRegisterClass(nodePath, name, insertAfter);
injectedRegister = true;
injectedClassNames.add(name);
}
// add decorators that don't require their import to be checked
addToSet(
baseClassNames,
baseClasses.filter(b => !b.import).map(b => b.name),
);
addToSet(
decoratorNames,
decorators.filter(c => !c.import).map(d => d.name),
);
program.traverse({
ImportDeclaration(importDecl) {
// find all base classes and decorators that match this import
const result = getImportedVariableNames(
baseClasses,
decorators,
importDecl,
resolvedFilename,
opts.rootDir,
);
addToSet(baseClassNames, result.baseClassNames);
addToSet(decoratorNames, result.decoratorNames);
},
});
program.traverse({
CallExpression(callExpr) {
const callee = callExpr.get('callee');
const args = callExpr.get('arguments');
if (!singlePath(callee) || !Array.isArray(args)) {
return;
}
if (callee.isMemberExpression()) {
// this might be a customElements.define call
const definedCustomElement = findDefinedCustomElement(callee, args);
if (!definedCustomElement) {
return;
}
if (definedCustomElement.isIdentifier()) {
maybeInjectRegister(callExpr, definedCustomElement.node.name);
}
if (definedCustomElement.isClassExpression()) {
// take inline class expression out of the define so that it can be registered
const id = callExpr.scope.generateUidIdentifierBasedOnNode(
definedCustomElement.node,
);
const { name } = id;
if (!injectedClassNames.has(name)) {
callExpr.insertBefore(
types.variableDeclaration('const', [
types.variableDeclarator(id, definedCustomElement.node),
]),
);
definedCustomElement.replaceWith(id);
maybeInjectRegister(callExpr, name);
}
}
return;
}
if (decoratorNames.size > 0) {
if (callee.isIdentifier()) {
// this might be a decorated class
const decoratedCustomElement = findDecoratedCustomElement(
decoratorNames,
callee,
args,
);
if (!decoratedCustomElement) {
return;
}
if (decoratedCustomElement.isIdentifier()) {
maybeInjectRegister(callExpr.parentPath, decoratedCustomElement.node.name);
}
}
}
},
ClassDeclaration(classDecl) {
// this is a class declaration like class A extends HTMLElement {}
if (implementsBaseClass(classDecl, baseClassNames)) {
maybeInjectRegister(classDecl, classDecl.node.id.name, true);
}
},
ClassExpression(classExpr) {
const { parent, parentPath: varDeclarator } = classExpr;
if (!parent || varDeclarator.isVariableDeclarator()) {
return;
}
if (!varDeclarator || varDeclarator.parentPath.isVariableDeclaration()) {
return;
}
const id = varDeclarator.get('id');
if (!singlePath(id) || !id.isIdentifier()) {
return;
}
const injectScope = varDeclarator.parentPath;
if (!injectScope) {
return;
}
// this is a class expression assignment like const A = class B extends HTMLElement {}
if (implementsBaseClass(classExpr, baseClassNames)) {
maybeInjectRegister(classExpr, id.node.name, true);
}
},
});
if (injectedRegister) {
injectRuntime(opts, program);
}
},
},
};
}
module.exports = babelPluginWcHmr;

View File

@@ -0,0 +1,40 @@
const { transformAsync } = require('@babel/core');
const babelPluginWcHmr = require('./babelPluginWcHmr');
const { createError } = require('../utils');
/** @typedef {import('./babelPluginWcHmr').BabelPluginWcHmrOptions} BabelPluginWcHmrOptions */
/**
* @param {string} code
* @param {string} filename
* @param {BabelPluginWcHmrOptions} options
* @returns {Promise<string>}
*/
async function babelTransform(code, filename, options) {
const largeFile = code.length > 100000;
const result = await transformAsync(code, {
caller: {
name: '@open-wc/dev-server-hmr',
supportsStaticESM: true,
},
plugins: [
[babelPluginWcHmr, options],
require.resolve('@babel/plugin-syntax-class-properties'),
require.resolve('@babel/plugin-syntax-import-assertions'),
require.resolve('@babel/plugin-syntax-top-level-await'),
],
filename,
babelrc: false,
configFile: false,
compact: largeFile,
sourceType: 'module',
});
if (!result?.code) {
throw createError(`Failed to babel transform ${filename}`);
}
return result.code;
}
module.exports = { babelTransform };

View File

@@ -0,0 +1,37 @@
/** @typedef {import('@babel/types').ClassDeclaration} ClassDeclaration */
/** @typedef {import('@babel/types').ClassExpression} ClassExpression */
/** @template T @typedef {import('@babel/core').NodePath<T>} NodePath<T> */
const { singlePath } = require('./utils');
/**
* @param {NodePath<ClassDeclaration> | NodePath<ClassExpression>} classDeclOrExpr
* @returns {NodePath<any> | undefined}
*/
function walkClassMixins(classDeclOrExpr) {
let el = classDeclOrExpr.get('superClass');
// walk possible mixin functions
while (singlePath(el) && el.isCallExpression()) {
const result = el.get('arguments');
if (Array.isArray(result)) {
[el] = result;
}
}
return singlePath(el) ? el : undefined;
}
/**
* @param {NodePath<ClassDeclaration> | NodePath<ClassExpression>} classDeclOrExpr
* @param {Set<string>} baseClassNames
*/
function implementsBaseClass(classDeclOrExpr, baseClassNames) {
const el = walkClassMixins(classDeclOrExpr);
if (el && el.isIdentifier()) {
const { name } = el.node;
return baseClassNames.has(name);
}
}
module.exports = { implementsBaseClass };

View File

@@ -0,0 +1,85 @@
/** @typedef {import('@babel/types').MemberExpression} MemberExpression */
/** @typedef {import('@babel/types').CallExpression} CallExpression */
/** @template T @typedef {import('@babel/core').NodePath<T>} NodePath<T> */
const { resolvePath, singlePath } = require('./utils');
const GLOBALS = ['window', 'self', 'globalThis'];
/**
* @param {NodePath<MemberExpression>} callee
* @param {NodePath<unknown>[]} args
*/
function isDefineCall(callee, args) {
const property = callee.get('property');
if (!singlePath(property) || !property.isIdentifier() || property.node.name !== 'define') {
return false;
}
if (!args || !Array.isArray(args)) {
return false;
}
return args.length >= 2 && (args[1].isIdentifier() || args[1].isClassExpression());
}
/** @param {NodePath<MemberExpression>} memberExpr */
function isCallOnCustomElementObject(memberExpr) {
const object = memberExpr.get('object');
if (!singlePath(object)) {
return false;
}
if (object.isIdentifier()) {
// we are dealing with <something>.define(), check if <something> references global customElements
const resolvedIdPath = resolvePath(object);
if (!resolvedIdPath || !resolvedIdPath.isIdentifier()) {
return false;
}
return resolvedIdPath.node.name === 'customElements';
}
if (object.isMemberExpression()) {
const property = object.get('property');
if (
!singlePath(property) ||
!property.isIdentifier() ||
property.node.name !== 'customElements'
) {
return false;
}
// we are dealing with <something>.customElements.define, check if <something> is the global scope
const subObject = object.get('object');
if (!singlePath(subObject) || !subObject.isIdentifier()) {
return false;
}
const resolvedIdPath = resolvePath(subObject);
return (
resolvedIdPath && resolvedIdPath.isIdentifier() && GLOBALS.includes(resolvedIdPath.node.name)
);
}
return false;
}
/** @param {NodePath<any>[]} args */
function getDefinedClass(args) {
if (!args || !Array.isArray(args)) {
return;
}
return args[1];
}
/**
* @param {NodePath<MemberExpression>} memberExpr
* @param {NodePath<any>[]} args
*/
function findDefinedCustomElement(memberExpr, args) {
if (isDefineCall(memberExpr, args) && isCallOnCustomElementObject(memberExpr)) {
return getDefinedClass(args);
}
}
module.exports = { findDefinedCustomElement };

View File

@@ -0,0 +1,61 @@
/** @typedef {import('@babel/types').Identifier} Identifier */
/** @typedef {import('./babelPluginWcHmr').Decorator} Decorator */
/** @template T @typedef {import('@babel/core').NodePath<T>} NodePath<T> */
const { singlePath } = require('./utils');
/**
* @param {Set<string>} decoratorNames
* @param {NodePath<Identifier>} callee
* @param {NodePath<any>[]} args
*/
function findCompiledTsDecoratedCustomElement(decoratorNames, callee, args) {
// is this a decorator helper function?
if (callee.node.name !== '__decorate') {
return;
}
const [arrayExpr, decoratedClass] = args;
// are we decorating an identifier (of a class)
if (!singlePath(decoratedClass) || !decoratedClass.isIdentifier()) {
return;
}
// is the first parameter an array of decorator functions?
if (!singlePath(arrayExpr) || !arrayExpr.isArrayExpression()) {
return;
}
const elements = arrayExpr.get('elements');
if (!Array.isArray(elements)) {
return;
}
// find a decorator function called customElement
const decoratorCall = elements.find(e => {
if (!e.isCallExpression()) {
return false;
}
const eCallee = e.get('callee');
if (!singlePath(eCallee) || !eCallee.isIdentifier()) {
return false;
}
return decoratorNames.has(eCallee.node.name);
});
if (!decoratorCall) {
return;
}
return decoratedClass;
}
/**
* @param {Set<string>} decoratorNames
* @param {NodePath<Identifier>} callee
* @param {NodePath<any>[]} args
* @returns {NodePath<any> | undefined}
*/
function findDecoratedCustomElement(decoratorNames, callee, args) {
// TODO: add non-compiled decorator when it becomes stage 3 and properly supported by babel
return findCompiledTsDecoratedCustomElement(decoratorNames, callee, args);
}
module.exports = { findDecoratedCustomElement };

View File

@@ -0,0 +1,100 @@
/** @typedef {import('@babel/types').ImportDefaultSpecifier} ImportDefaultSpecifier */
/** @typedef {import('@babel/types').ImportNamespaceSpecifier} ImportNamespaceSpecifier */
/** @typedef {import('@babel/types').ImportSpecifier} ImportSpecifier */
/** @typedef {import('@babel/types').ImportDeclaration} ImportDeclaration */
/** @typedef {import('@babel/types').StringLiteral} StringLiteral */
/** @template T @typedef {import('@babel/core').NodePath<T>} NodePath */
/** @typedef {import('./babelPluginWcHmr').BaseClass} BaseClass */
/** @typedef {import('./babelPluginWcHmr').Decorator} Decorator */
const path = require('path');
const { singlePath } = require('./utils');
/**
* @param {string} importSpecifier
* @param {string} importPath
* @param {string} filename
* @param {string} rootDir
* @returns {boolean}
*/
function isMatchingImport(importSpecifier, importPath, filename, rootDir) {
// is this a non-bare import
if (
importSpecifier.startsWith('../') ||
importSpecifier.startsWith('./') ||
importSpecifier.startsWith('/')
) {
const partialImportFilePath = importSpecifier.split('/').join(path.sep);
const joinBase = importSpecifier.startsWith('/') ? rootDir : path.dirname(filename);
const importFilePath = path.join(joinBase, partialImportFilePath);
return importFilePath === importPath || `${importFilePath}.js` === importPath;
}
// this is a bare import
return importPath === importSpecifier || `${importPath}.js` === importSpecifier;
}
/**
* @param {BaseClass[]} baseClasses
* @param {Decorator[]} decorators
* @param {string} importSpecifier
* @param {string} filename
* @param {string} rootDir
*/
function getMatchesForImport(baseClasses, decorators, importSpecifier, filename, rootDir) {
return {
baseClasses: baseClasses.filter(
baseClass =>
baseClass.import && isMatchingImport(importSpecifier, baseClass.import, filename, rootDir),
),
decorators: decorators.filter(
decorator =>
decorator.import && isMatchingImport(importSpecifier, decorator.import, filename, rootDir),
),
};
}
/**
* @param {BaseClass[]} baseClasses
* @param {Decorator[]} decorators
* @param {NodePath<ImportDeclaration>} importDeclaration
* @param {string} filename
* @param {string} rootDir
*/
function getImportedVariableNames(baseClasses, decorators, importDeclaration, filename, rootDir) {
const importSpecifier = importDeclaration.node.source.value;
const matches = getMatchesForImport(baseClasses, decorators, importSpecifier, filename, rootDir);
/** @type {string[]} */
const baseClassNames = [];
const decoratorNames = [];
if (matches.baseClasses.length || matches.decorators.length) {
for (const specifier of importDeclaration.get('specifiers')) {
if (specifier.isImportDefaultSpecifier()) {
if (matches.baseClasses.some(cl => cl.name === 'default')) {
baseClassNames.push(specifier.node.local.name);
}
if (matches.decorators.some(cl => cl.name === 'default')) {
decoratorNames.push(specifier.node.local.name);
}
} else if (specifier.isImportSpecifier()) {
const imported = specifier.get('imported');
if (singlePath(imported)) {
const importedName = imported.isIdentifier()
? imported.node.name
: /** @type {StringLiteral} */ (imported.node).value;
if (matches.baseClasses.some(cl => cl.name === importedName)) {
baseClassNames.push(specifier.node.local.name);
}
if (matches.decorators.some(cl => cl.name === importedName)) {
decoratorNames.push(specifier.node.local.name);
}
}
}
}
}
return { baseClassNames, decoratorNames };
}
module.exports = { getImportedVariableNames };

View File

@@ -0,0 +1,40 @@
/** @typedef {import('@babel/types').Program} Program */
/** @typedef {import('./babelPluginWcHmr').BabelPluginWcHmrOptions} BabelPluginWcHmrOptions */
/** @template T @typedef {import('@babel/core').NodePath<T>} NodePath<T> */
const { parse } = require('@babel/core');
const { WC_HMR_NAMESPACE, WC_HMR_MODULE_PATCH, WC_HMR_MODULE_RUNTIME } = require('../constants');
/**
* @template T
* @param {NodePath<T>} nodePath
* @param {string} name
* @param {boolean} insertAfter
*/
function injectRegisterClass(nodePath, name, insertAfter) {
const toInject = parse(`${WC_HMR_NAMESPACE}.register(import.meta.url, ${name})`);
if (!toInject) throw new TypeError('Failed to parse');
if (insertAfter) {
nodePath.insertAfter(toInject);
} else {
nodePath.insertBefore(toInject);
}
}
/**
* @param {BabelPluginWcHmrOptions} options
* @param {NodePath<Program>} program
*/
function injectRuntime(options, program) {
const patch =
options.patches && options.patches.length > 0 ? `import '${WC_HMR_MODULE_PATCH}'; ` : '';
const toInject = parse(
`${patch}import * as ${WC_HMR_NAMESPACE} from '${WC_HMR_MODULE_RUNTIME}'; if(import.meta.hot) { import.meta.hot.accept(); }`,
);
if (toInject) {
program.node.body.unshift(/** @type {any} */ (toInject));
}
}
module.exports = { injectRegisterClass, injectRuntime };

View File

@@ -0,0 +1,45 @@
/** @typedef {import('./babelPluginWcHmr').BabelPluginWcHmrOptions} BabelPluginWcHmrOptions */
/** @template T @typedef {import('@babel/core').NodePath<T>} NodePath<T> */
const { createError } = require('../utils');
/**
* @param {BabelPluginWcHmrOptions} options
* @returns {BabelPluginWcHmrOptions}
*/
function parseOptions(options) {
if (!options) throw createError('Missing babel plugin options');
if (!options.rootDir) throw createError('Missing rootDir in babel plugin options');
return options;
}
/**
* @template T
* @param {NodePath<T>} nodePath
*/
function resolvePath(nodePath) {
const pathCast = /** @type {any} */ (nodePath);
return /** @type {NodePath<unknown>} */ (pathCast.resolve());
}
/**
* @template T
* @param {NodePath<T> | NodePath<T>[]} nodePath
* @returns {nodePath is NodePath<T>}
*/
function singlePath(nodePath) {
return nodePath && !Array.isArray(nodePath);
}
/**
* @template T
* @param {Set<T>} set
* @param {T[]} elements
*/
function addToSet(set, elements) {
for (const e of elements) {
set.add(e);
}
}
module.exports = { parseOptions, resolvePath, singlePath, addToSet };

View File

@@ -0,0 +1,11 @@
const WC_HMR_NAMESPACE = '__$wc_hmr$__';
const WC_HMR_MODULE_PREFIX = '/__web-dev-server__/wc-hmr/';
const WC_HMR_MODULE_PATCH = `${WC_HMR_MODULE_PREFIX}patch.js`;
const WC_HMR_MODULE_RUNTIME = `${WC_HMR_MODULE_PREFIX}runtime.js`;
module.exports = {
WC_HMR_NAMESPACE,
WC_HMR_MODULE_PREFIX,
WC_HMR_MODULE_PATCH,
WC_HMR_MODULE_RUNTIME,
};

View File

@@ -0,0 +1,149 @@
/** @typedef {import('@web/dev-server-core').Plugin} DevServerPlugin */
/** @typedef {import('./utils').Matcher} Matcher */
/**
* @typedef {object} BaseClass
* @property {string} name
* @property {string} [import]
*/
/**
* @typedef {object} Decorator
* @property {string} name
* @property {string} [import]
*/
/**
* @typedef {object} Preset
* @property {BaseClass[]} baseClasses
* @property {Decorator[]} decorators
* @property {string} patch
*/
/**
* @typedef {object} WcHmrPluginConfig
* @property {string[]} [include]
* @property {string[]} [exclude]
* @property {Preset[]} [presets]
* @property {BaseClass[]} [baseClasses]
* @property {Decorator[]} [decorators]
* @property {string[]} [patches]
*/
const { getRequestFilePath, PluginSyntaxError } = require('@web/dev-server-core');
const { hmrPlugin: createBaseHmrPlugin } = require('@web/dev-server-hmr');
const { WC_HMR_MODULE_PREFIX, WC_HMR_MODULE_RUNTIME, WC_HMR_MODULE_PATCH } = require('./constants');
const { parseConfig, createMatchers, createError } = require('./utils');
const { babelTransform } = require('./babel/babelTransform');
const { wcHmrRuntime } = require('./wcHmrRuntime');
/**
* @param {WcHmrPluginConfig} pluginConfig
* @returns {DevServerPlugin}
*/
function hmrPlugin(pluginConfig) {
const baseHmrPlugin = createBaseHmrPlugin();
const parsedPluginConfig = parseConfig(pluginConfig);
/** @type {string} */
let rootDir;
/** @type {Matcher} */
let matchInclude = () => true;
/** @type {Matcher} */
let matchExclude = () => false;
return {
name: 'wc-hmr',
injectWebSocket: true,
resolveImport(...args) {
const { source } = args[0];
if (source.startsWith(WC_HMR_MODULE_PREFIX)) {
return source;
}
return baseHmrPlugin.resolveImport?.(...args);
},
serve(...args) {
const context = args[0];
if (context.path === WC_HMR_MODULE_RUNTIME) {
return wcHmrRuntime;
}
if (context.path === WC_HMR_MODULE_PATCH) {
return pluginConfig.patches?.join('\n') || '';
}
return baseHmrPlugin.serve?.(...args);
},
serverStart(...args) {
if (args[0].config.plugins?.find(pl => pl.name === 'hmr')) {
throw createError(
`Cannot include both @web/dev-server-hmr and @open-wc/dev-server-hmr plugins.`,
);
}
rootDir = args[0].config.rootDir;
if (parsedPluginConfig.include) {
matchInclude = createMatchers(rootDir, parsedPluginConfig.include);
}
if (parsedPluginConfig.exclude) {
matchExclude = createMatchers(rootDir, parsedPluginConfig.exclude);
}
return baseHmrPlugin.serverStart?.(...args);
},
async transform(...args) {
const context = args[0];
if (!context.response.is('js')) {
return;
}
const filePath = getRequestFilePath(context, rootDir);
if (
matchInclude(filePath) &&
!matchExclude(filePath) &&
!filePath.startsWith('__web-dev-server__')
) {
try {
context.body = await babelTransform(context.body, filePath, {
baseClasses: parsedPluginConfig.baseClasses,
decorators: parsedPluginConfig.decorators,
patches: parsedPluginConfig.patches,
rootDir,
});
} catch (error) {
if (error.name === 'SyntaxError') {
// forward babel error to dev server
const strippedMsg = error.message.replace(new RegExp(`${filePath} ?:? ?`, 'g'), '');
throw new PluginSyntaxError(strippedMsg, filePath, error.code, error.loc, error.pos);
}
throw error;
}
}
return baseHmrPlugin.transform?.(...args);
},
// forward all other plugin hooks
serverStop(...args) {
return baseHmrPlugin.serverStop?.(...args);
},
transformCacheKey(...args) {
return baseHmrPlugin.transformCacheKey?.(...args);
},
transformImport(...args) {
return baseHmrPlugin.transformImport?.(...args);
},
resolveMimeType(...args) {
return baseHmrPlugin.resolveMimeType?.(...args);
},
};
}
module.exports = { hmrPlugin };

View File

@@ -0,0 +1,12 @@
const { hmrPlugin } = require('./hmrPlugin');
const { litElement } = require('./presets/litElement');
const { fastElement } = require('./presets/fastElement');
const { WC_HMR_MODULE_RUNTIME } = require('./constants');
const presets = { litElement, fastElement };
module.exports = {
hmrPlugin,
presets,
WC_HMR_MODULE_RUNTIME,
};

View File

@@ -0,0 +1,20 @@
const { WC_HMR_MODULE_RUNTIME } = require('../constants');
const patch = `import { FASTElement, FASTElementDefinition } from '@microsoft/fast-element';
import { updateClassMembers } from '${WC_HMR_MODULE_RUNTIME}';
FASTElement.prototype.hotReplaceCallback = function hotReplaceCallback(newClass) {
const newDefinition = FASTElementDefinition.forType(newClass);
if (newDefinition) {
this.$fastController.styles = newDefinition.styles;
this.$fastController.template = newDefinition.template;
}
};`;
const fastElement = {
decorators: [{ name: 'customElement', import: '@microsoft/fast-element' }],
baseClasses: [{ name: 'FASTElement', import: '@microsoft/fast-element' }],
patch,
};
module.exports = { fastElement };

View File

@@ -0,0 +1,41 @@
const { WC_HMR_MODULE_RUNTIME } = require('../constants');
const patch = `import { LitElement } from 'lit-element';
import { updateClassMembers } from '${WC_HMR_MODULE_RUNTIME}';
const supportsAdoptingStyleSheets = (window.ShadowRoot) &&
(window.ShadyCSS === undefined || window.ShadyCSS.nativeShadow) &&
('adoptedStyleSheets' in Document.prototype) &&
('replace' in CSSStyleSheet.prototype);
// static callback
LitElement.hotReplaceCallback = function hotReplaceCallback(newClass) {
newClass.finalize();
updateClassMembers(this, newClass);
this.finalize();
};
// instance callback
LitElement.prototype.hotReplaceCallback = function hotReplaceCallback() {
if (!supportsAdoptingStyleSheets) {
const nodes = Array.from(this.renderRoot.children);
for (const node of nodes) {
if (node.tagName.toLowerCase() === 'style') {
node.remove();
}
}
}
this.constructor._getUniqueStyles();
if (window.ShadowRoot && this.renderRoot instanceof window.ShadowRoot) {
this.adoptStyles();
}
this.requestUpdate();
};`;
const litElement = {
decorators: [{ name: 'customElement', import: 'lit-element' }],
baseClasses: [{ name: 'LitElement', import: 'lit-element' }],
patch,
};
module.exports = { litElement };

View File

@@ -0,0 +1,87 @@
/* eslint-disable no-param-reassign */
const picoMatch = require('picomatch');
const { isAbsolute, posix, sep } = require('path');
/** @typedef {(path: string) => boolean} Matcher */
/** @typedef {import('./hmrPlugin').WcHmrPluginConfig} WcHmrPluginConfig */
/**
* @param {string} msg
*/
function createError(msg) {
return new Error(`[@open-wc/dev-server-hmr] ${msg}`);
}
/**
* @param {WcHmrPluginConfig} config
* @param {keyof WcHmrPluginConfig} prop
*/
function assertOptionalArray(config, prop) {
if (config[prop] != null && !Array.isArray(config[prop])) {
throw createError(`Option ${prop} must be an array`);
}
}
/**
* @param {WcHmrPluginConfig} config
*/
function parseConfig(config) {
if (!Array.isArray(config.include) && !Array.isArray(config.exclude)) {
throw createError('Must provide either an "include" or "exclude" pattern in config.');
}
assertOptionalArray(config, 'include');
assertOptionalArray(config, 'exclude');
assertOptionalArray(config, 'patches');
assertOptionalArray(config, 'baseClasses');
assertOptionalArray(config, 'decorators');
assertOptionalArray(config, 'presets');
config.baseClasses = config.baseClasses || [];
config.baseClasses.push({ name: 'HTMLElement' });
if (config.presets) {
for (const preset of config.presets) {
if (preset.patch) {
config.patches = config.patches || [];
config.patches.push(preset.patch);
}
if (preset.baseClasses) {
config.baseClasses.push(...preset.baseClasses);
}
if (preset.decorators) {
config.decorators = config.decorators || [];
config.decorators.push(...preset.decorators);
}
}
}
return { ...config };
}
/**
* @param {string} rootDir
* @param {string} pattern
* @returns {Matcher}
*/
function createMatcher(rootDir, pattern) {
const matcherRootDir = rootDir.split(sep).join('/');
const resolvedPattern =
!isAbsolute(pattern) && !pattern.startsWith('*')
? posix.join(matcherRootDir, pattern)
: pattern;
return picoMatch(resolvedPattern);
}
/**
* @param {string} rootDir
* @param {string[]} patterns
* @returns {Matcher}
*/
function createMatchers(rootDir, patterns) {
const matchers = patterns.map(p => createMatcher(rootDir, p));
return function matcher(path) {
return matchers.some(m => m(path));
};
}
module.exports = { createMatcher, createMatchers, parseConfig, createError };

View File

@@ -0,0 +1,96 @@
const wcHmrRuntime = `
// override global define to allow double registrations
const originalDefine = window.customElements.define;
window.customElements.define = (name, ...rest) => {
if (!window.customElements.get(name)) {
originalDefine.call(window.customElements, name, ...rest);
}
};
const registry = new Map();
function createClassKey(importMetaUrl, className) {
const modulePath = new URL(importMetaUrl).pathname;
return \`\${modulePath}:\${className}\`;
}
export function register(importMetaUrl, hmrClass) {
const key = createClassKey(importMetaUrl, hmrClass.name);
const hotReplaceCallback = registry.get(key);
if (hotReplaceCallback) {
// class is already registered, call the replace function registered below
hotReplaceCallback(hmrClass);
return;
}
// class is not yet registered, set it up and register an update callback
const connectedElements = trackConnectedElements(hmrClass);
// register a callback for this class later patch in updates to the class
registry.set(key, async newClass => {
// wait 1 microtask to allow the class definition to propagate
await 0;
if (hmrClass.hotReplaceCallback) {
// class has implemented a callback, use that
hmrClass.hotReplaceCallback(newClass);
} else {
// otherwise apply default update
updateClassMembers(hmrClass, newClass);
}
for (const element of connectedElements) {
if (element.hotReplaceCallback) {
element.hotReplaceCallback(newClass);
}
}
});
}
function trackConnectedElements(hmrClass) {
const connectedElements = new Set();
const originalCb = hmrClass.prototype.connectedCallback;
hmrClass.prototype.connectedCallback = function connectedCallback(...args) {
if (originalCb) {
originalCb.call(this, ...args);
}
connectedElements.add(this);
};
const originalDcb = hmrClass.prototype.disconnectedCallback;
hmrClass.prototype.disconnectedCallback = function disconnectedCallback(...args) {
if (originalDcb) {
originalDcb.call(this, ...args);
}
connectedElements.delete(this);
};
return connectedElements;
}
const preserved = ['connectedCallback', 'disconnectedCallback', 'observedAttributes'];
export function updateClassMembers(hmrClass, newClass) {
updateObjectMembers(hmrClass, newClass);
updateObjectMembers(hmrClass.prototype, newClass.prototype);
}
export function updateObjectMembers(hmrClass, newClass) {
const currentProperties = new Set(Object.getOwnPropertyNames(hmrClass));
const newProperties = new Set(Object.getOwnPropertyNames(newClass));
for (const prop of Object.getOwnPropertyNames(newClass)) {
const descriptor = Object.getOwnPropertyDescriptor(newClass, prop);
if (descriptor && descriptor.configurable) {
Object.defineProperty(hmrClass, prop, descriptor);
}
}
for (const existingProp of currentProperties) {
if (!preserved.includes(existingProp) && !newProperties.has(existingProp)) {
try {
delete hmrClass[existingProp];
} catch {}
}
}
}`;
module.exports = { wcHmrRuntime };

View File

@@ -0,0 +1,126 @@
/* eslint-disable import/no-extraneous-dependencies */
const { expect } = require('chai');
const path = require('path');
const { banner, transform, rootDir } = require('./utils');
describe('babelPluginWcHmr - detecting base class', () => {
it('global base class', () => {
const code = `class Foo extends MyElement {}`;
const result = transform(code, { baseClasses: [{ name: 'MyElement' }] });
expect(result).to.equal(`${banner}
class Foo extends MyElement {}
__$wc_hmr$__.register(import.meta.url, Foo);`);
});
it('named import', () => {
const code = `import { MyElement } from 'my-element'; class Foo extends MyElement {}`;
const result = transform(code, { baseClasses: [{ name: 'MyElement', import: 'my-element' }] });
expect(result).to.equal(`${banner}
import { MyElement } from 'my-element';
class Foo extends MyElement {}
__$wc_hmr$__.register(import.meta.url, Foo);`);
});
it('unmatched import', () => {
const code = `import { MyElement } from 'not-my-element'; class Foo extends MyElement {}`;
const result = transform(code, { baseClasses: [{ name: 'MyElement', import: 'my-element' }] });
expect(result).to.equal(`import { MyElement } from 'not-my-element';
class Foo extends MyElement {}`);
});
it('default import', () => {
const code = `import BaseElement from 'base-element'; class Foo extends BaseElement {}`;
const result = transform(code, { baseClasses: [{ name: 'default', import: 'base-element' }] });
expect(result).to.equal(`${banner}
import BaseElement from 'base-element';
class Foo extends BaseElement {}
__$wc_hmr$__.register(import.meta.url, Foo);`);
});
it('mixins', () => {
const code = `import { MyElement } from 'my-element'; class Foo extends MixA(MixB(MyElement)) {}`;
const result = transform(code, { baseClasses: [{ name: 'MyElement', import: 'my-element' }] });
expect(result).to.equal(`${banner}
import { MyElement } from 'my-element';
class Foo extends MixA(MixB(MyElement)) {}
__$wc_hmr$__.register(import.meta.url, Foo);`);
});
it('multiple classes', () => {
const code = `import { MyElement } from 'my-element';
class A extends MyElement {}
class B {}
class C extends HTMLElement {}
class D extends MyElement {}`;
const result = transform(code, { baseClasses: [{ name: 'MyElement', import: 'my-element' }] });
expect(result).to.equal(`${banner}
import { MyElement } from 'my-element';
class A extends MyElement {}
__$wc_hmr$__.register(import.meta.url, A);
class B {}
class C extends HTMLElement {}
class D extends MyElement {}
__$wc_hmr$__.register(import.meta.url, D);`);
});
it('multiple base class definitions', () => {
const code = `import { ElementA } from 'element-a';
import ElementB from 'element-b';
class A extends ElementA {}
class B extends ElementB {}
class C extends HTMLElement {}`;
const result = transform(code, {
baseClasses: [
{ name: 'ElementA', import: 'element-a' },
{ name: 'default', import: 'element-b' },
],
});
expect(result).to.equal(`${banner}
import { ElementA } from 'element-a';
import ElementB from 'element-b';
class A extends ElementA {}
__$wc_hmr$__.register(import.meta.url, A);
class B extends ElementB {}
__$wc_hmr$__.register(import.meta.url, B);
class C extends HTMLElement {}`);
});
it('base class with a specific import path', () => {
const code = "import { MyElement } from '../MyElement.js'; class Foo extends MyElement {}";
const result = transform(code, {
baseClasses: [{ name: 'MyElement', import: path.join(rootDir, 'MyElement.js') }],
});
expect(result).to.equal(`${banner}
import { MyElement } from '../MyElement.js';
class Foo extends MyElement {}
__$wc_hmr$__.register(import.meta.url, Foo);`);
});
});

View File

@@ -0,0 +1,138 @@
/* eslint-disable import/no-extraneous-dependencies */
const { expect } = require('chai');
const { banner, transform } = require('./utils');
describe('babelPluginWcHmr - detecting customElements.define', () => {
it('injects registration when detecting a customElements.define', () => {
const code = `class Foo extends HTMLElement {}\ncustomElements.define('x-foo', Foo);`;
const result = transform(code);
expect(result).to.equal(
`${banner}
class Foo extends HTMLElement {}
__$wc_hmr$__.register(import.meta.url, Foo);
customElements.define('x-foo', Foo);`,
);
});
it('injects registration when detecting a window.customElements.define', () => {
const code = `class Foo extends HTMLElement {}\nwindow.customElements.define('x-foo', Foo);`;
const result = transform(code);
expect(result).to.equal(
`${banner}
class Foo extends HTMLElement {}
__$wc_hmr$__.register(import.meta.url, Foo);
window.customElements.define('x-foo', Foo);`,
);
});
it('injects registration when detecting a globalThis.customElements.define', () => {
const code = `class Foo extends HTMLElement {}\nglobalThis.customElements.define('x-foo', Foo);`;
const result = transform(code);
expect(result).to.equal(
`${banner}
class Foo extends HTMLElement {}
__$wc_hmr$__.register(import.meta.url, Foo);
globalThis.customElements.define('x-foo', Foo);`,
);
});
it('injects multiple registrations', () => {
const code =
"class Foo extends HTMLElement {}\ncustomElements.define('x-foo', Foo);\n" +
"class Bar extends HTMLElement {}\ncustomElements.define('x-bar', Bar);" +
"class Baz extends HTMLElement {}\ncustomElements.define('x-baz', Baz);";
// console.log(code);
const result = transform(code);
expect(result).to.equal(
`${banner}
class Foo extends HTMLElement {}
__$wc_hmr$__.register(import.meta.url, Foo);
customElements.define('x-foo', Foo);
class Bar extends HTMLElement {}
__$wc_hmr$__.register(import.meta.url, Bar);
customElements.define('x-bar', Bar);
class Baz extends HTMLElement {}
__$wc_hmr$__.register(import.meta.url, Baz);
customElements.define('x-baz', Baz);`,
);
});
it('can configure a patch to be injected', () => {
const code = `class Foo extends HTMLElement {}\ncustomElements.define('x-foo', Foo);`;
const result = transform(code, { patches: ['x.js'] });
expect(result).to.equal(
`import '/__web-dev-server__/wc-hmr/patch.js';
${banner}
class Foo extends HTMLElement {}
__$wc_hmr$__.register(import.meta.url, Foo);
customElements.define('x-foo', Foo);`,
);
});
it('injects registration when detecting an inline class', () => {
const code = `customElements.define('x-foo', class Foo extends HTMLElement {});`;
const result = transform(code);
expect(result).to.equal(`${banner}
const _Foo = class Foo extends HTMLElement {};
__$wc_hmr$__.register(import.meta.url, _Foo);
customElements.define('x-foo', _Foo);`);
});
it('injects registration when detecting an anonymous inline class', () => {
const code = `customElements.define('x-foo', class extends HTMLElement {});`;
const result = transform(code);
expect(result).to.equal(`${banner}
const _ref = class extends HTMLElement {};
__$wc_hmr$__.register(import.meta.url, _ref);
customElements.define('x-foo', _ref);`);
});
it('deconflicts variable names', () => {
const code = `const Foo = 1; const _Foo = 2; customElements.define('x-foo', class Foo extends HTMLElement {});`;
const result = transform(code);
expect(result).to.equal(`${banner}
const Foo = 1;
const _Foo = 2;
const _Foo2 = class Foo extends HTMLElement {};
__$wc_hmr$__.register(import.meta.url, _Foo2);
customElements.define('x-foo', _Foo2);`);
});
it('respects the custom element define scope', () => {
const code = `console.log("x"); (function () { customElements.define('x-foo', class extends HTMLElement {}); })();`;
const result = transform(code);
expect(result).to.equal(`${banner}
console.log("x");
(function () {
const _ref = class extends HTMLElement {};
__$wc_hmr$__.register(import.meta.url, _ref);
customElements.define('x-foo', _ref);
})();`);
});
});

View File

@@ -0,0 +1,115 @@
/* eslint-disable import/no-extraneous-dependencies */
const { expect } = require('chai');
const path = require('path');
const { banner, transform, rootDir } = require('./utils');
describe('babelPluginWcHmr - detecting decorators', () => {
it('compiled decorator with decorator name', () => {
const code = `
function __decorate() {}
let Foo = class extends HTMLElement {};
Foo = __decorate([customElement('x-foo')], Foo);`;
const result = transform(code, { decorators: [{ name: 'customElement' }] });
expect(result).to.equal(`${banner}
function __decorate() {}
let Foo = class extends HTMLElement {};
__$wc_hmr$__.register(import.meta.url, Foo);
Foo = __decorate([customElement('x-foo')], Foo);`);
});
it('handles multiple decorators', () => {
const code = `
function __decorate() {}
let Foo = class extends HTMLElement {};
Foo = __decorate([x(), y(), customElement('x-foo')], Foo);`;
const result = transform(code, { decorators: [{ name: 'customElement' }] });
expect(result).to.equal(`${banner}
function __decorate() {}
let Foo = class extends HTMLElement {};
__$wc_hmr$__.register(import.meta.url, Foo);
Foo = __decorate([x(), y(), customElement('x-foo')], Foo);`);
});
it('does not inject for a decorator with a different name', () => {
const code = `function __decorate() {}
let Foo = class extends HTMLElement {};
Foo = __decorate([notCustomElement('x-foo')], Foo);`;
const result = transform(code, { decorators: [{ name: 'customElement' }] });
expect(result).to.equal(code);
});
it('compiled decorator with a bare import', () => {
const code = `
import {customElement} from 'my-package';
function __decorate() {}
let Foo = class extends HTMLElement {};
Foo = __decorate([customElement('x-foo')], Foo);`;
const result = transform(code, {
decorators: [{ name: 'customElement', import: 'my-package' }],
});
expect(result).to.equal(`${banner}
import { customElement } from 'my-package';
function __decorate() {}
let Foo = class extends HTMLElement {};
__$wc_hmr$__.register(import.meta.url, Foo);
Foo = __decorate([customElement('x-foo')], Foo);`);
});
it('compiled decorator with a default import', () => {
const code = `
import customElement from 'my-package';
function __decorate() {}
let Foo = class extends HTMLElement {};
Foo = __decorate([customElement('x-foo')], Foo);`;
const result = transform(code, {
decorators: [{ name: 'default', import: 'my-package' }],
});
expect(result).to.equal(`${banner}
import customElement from 'my-package';
function __decorate() {}
let Foo = class extends HTMLElement {};
__$wc_hmr$__.register(import.meta.url, Foo);
Foo = __decorate([customElement('x-foo')], Foo);`);
});
it('compiled decorator with a specific import path', () => {
const code = `
import { defineElement } from '../defineElement.js';
function __decorate() {}
let Foo = class extends HTMLElement {};
Foo = __decorate([defineElement('x-foo')], Foo);`;
const result = transform(code, {
decorators: [{ name: 'defineElement', import: path.join(rootDir, 'defineElement.js') }],
});
expect(result).to.equal(`${banner}
import { defineElement } from '../defineElement.js';
function __decorate() {}
let Foo = class extends HTMLElement {};
__$wc_hmr$__.register(import.meta.url, Foo);
Foo = __decorate([defineElement('x-foo')], Foo);`);
});
});

View File

@@ -0,0 +1,27 @@
const { transformSync } = require('@babel/core');
const path = require('path');
const babelPluginWcHmr = require('../../src/babel/babelPluginWcHmr');
const banner = `import * as __$wc_hmr$__ from '/__web-dev-server__/wc-hmr/runtime.js';
if (import.meta.hot) {
import.meta.hot.accept();
}
`;
const rootDir = path.join(process.cwd(), 'virtual-project');
/**
* @param {string} code
* @returns {string}
*/
function transform(code, extraOptions) {
return transformSync(code, {
babelrc: false,
configFile: false,
filename: path.join(rootDir, 'src', 'foo.js'),
plugins: [[babelPluginWcHmr, { rootDir, ...extraOptions }]],
}).code;
}
module.exports = { banner, transform, rootDir };

View File

@@ -0,0 +1,24 @@
// Don't edit this file directly. It is generated by /scripts/update-package-configs.ts
{
"extends": "../../tsconfig.node-base.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "./types",
"rootDir": ".",
"composite": true,
"allowJs": true,
"strict": true,
"checkJs": true,
"emitDeclarationOnly": true
},
"references": [],
"include": [
"src",
"*.js"
],
"exclude": [
"dist",
"types"
]
}

View File

@@ -48,7 +48,7 @@
"@types/babel__core": "^7.1.3", "@types/babel__core": "^7.1.3",
"@types/chai": "^4.2.11", "@types/chai": "^4.2.11",
"@types/mocha": "^5.0.0", "@types/mocha": "^5.0.0",
"@web/dev-server": "^0.0.19", "@web/dev-server": "^0.0.24",
"@web/dev-server-storybook": "^0.1.3", "@web/dev-server-storybook": "^0.1.3",
"babel-loader": "^8.0.0", "babel-loader": "^8.0.0",
"mocha-chai-snapshot": "^1.0.0" "mocha-chai-snapshot": "^1.0.0"

View File

@@ -80,6 +80,7 @@ packageDirnameMap.forEach((packageDirname, packageName) => {
rootDir: '.', rootDir: '.',
composite: true, composite: true,
allowJs: true, allowJs: true,
strict: pkg.strict,
checkJs: pkg.type === 'js' ? true : undefined, checkJs: pkg.type === 'js' ? true : undefined,
emitDeclarationOnly: pkg.type === 'js' ? true : undefined, emitDeclarationOnly: pkg.type === 'js' ? true : undefined,
}, },

View File

@@ -10,6 +10,9 @@
{ {
"path": "./packages/polyfills-loader/tsconfig.json" "path": "./packages/polyfills-loader/tsconfig.json"
}, },
{
"path": "./packages/dev-server-hmr/tsconfig.json"
},
{ {
"path": "./packages/import-maps-resolve/tsconfig.json" "path": "./packages/import-maps-resolve/tsconfig.json"
}, },

View File

@@ -1,5 +1,6 @@
const packages = [ const packages = [
{ name: 'polyfills-loader', type: 'js', environment: 'node' }, { name: 'polyfills-loader', type: 'js', environment: 'node' },
{ name: 'dev-server-hmr', type: 'js', environment: 'node', strict: true },
{ name: 'import-maps-resolve', type: 'js', environment: 'node' }, { name: 'import-maps-resolve', type: 'js', environment: 'node' },
{ name: 'scoped-elements', type: 'js', environment: 'browser' }, { name: 'scoped-elements', type: 'js', environment: 'browser' },
{ name: 'testing-helpers', type: 'js', environment: 'browser' }, { name: 'testing-helpers', type: 'js', environment: 'browser' },

341
yarn.lock
View File

@@ -361,16 +361,16 @@
chalk "^2.0.0" chalk "^2.0.0"
js-tokens "^4.0.0" js-tokens "^4.0.0"
"@babel/parser@*", "@babel/parser@^7.12.5":
version "7.12.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0"
integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.10.4", "@babel/parser@^7.11.5", "@babel/parser@^7.12.0", "@babel/parser@^7.12.1", "@babel/parser@^7.12.3", "@babel/parser@^7.4.2", "@babel/parser@^7.4.3", "@babel/parser@^7.6.0", "@babel/parser@^7.7.0", "@babel/parser@^7.9.6": "@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.10.4", "@babel/parser@^7.11.5", "@babel/parser@^7.12.0", "@babel/parser@^7.12.1", "@babel/parser@^7.12.3", "@babel/parser@^7.4.2", "@babel/parser@^7.4.3", "@babel/parser@^7.6.0", "@babel/parser@^7.7.0", "@babel/parser@^7.9.6":
version "7.12.3" version "7.12.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.3.tgz#a305415ebe7a6c7023b40b5122a0662d928334cd" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.3.tgz#a305415ebe7a6c7023b40b5122a0662d928334cd"
integrity sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw== integrity sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==
"@babel/parser@^7.12.5":
version "7.12.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0"
integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==
"@babel/plugin-proposal-async-generator-functions@^7.12.1": "@babel/plugin-proposal-async-generator-functions@^7.12.1":
version "7.12.1" version "7.12.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz#dc6c1170e27d8aca99ff65f4925bd06b1c90550e" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz#dc6c1170e27d8aca99ff65f4925bd06b1c90550e"
@@ -522,6 +522,13 @@
dependencies: dependencies:
"@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-import-assertions@^7.12.1":
version "7.12.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.12.1.tgz#fd96f8f7bb681fa7dacfe470fa41add68f7f241f"
integrity sha512-eyHdg2/DBrQfPgT7URyQacH0BuR2yJoKPkc5yQwRSh6yfA+77A5JRRGxwSOWHpsitWxtimHfPqJ+sZA2DuDJLg==
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.2.0": "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.2.0":
version "7.10.4" version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
@@ -1735,6 +1742,13 @@
dependencies: dependencies:
extend "3.0.2" extend "3.0.2"
"@mdn/browser-compat-data@^2.0.7":
version "2.0.7"
resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-2.0.7.tgz#72ec37b9c1e00ce0b4e0309d753be18e2da12ee3"
integrity sha512-GeeM827DlzFFidn1eKkMBiqXFD2oLsnZbaiGhByPl0vcapsRzUL+t9hDoov1swc9rB2jw64R+ihtzC8qOE9wXw==
dependencies:
extend "3.0.2"
"@mdx-js/loader@^1.5.1": "@mdx-js/loader@^1.5.1":
version "1.6.19" version "1.6.19"
resolved "https://registry.yarnpkg.com/@mdx-js/loader/-/loader-1.6.19.tgz#2b90dee54b6f959539f310776f18fe24e1f15cc5" resolved "https://registry.yarnpkg.com/@mdx-js/loader/-/loader-1.6.19.tgz#2b90dee54b6f959539f310776f18fe24e1f15cc5"
@@ -1809,6 +1823,11 @@
resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.21.tgz#25f97a0a1b76e78c16ae5d98c6c73e1be8d89e39" resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.21.tgz#25f97a0a1b76e78c16ae5d98c6c73e1be8d89e39"
integrity sha512-6sANhqfEHu6gdHZSrzDjN18Y48mIon8f2Os6J+IFmMHN0IhNG/0PUIIsI07kA1sZ9t6vgZNBloVmcDa5WOSe6A== integrity sha512-6sANhqfEHu6gdHZSrzDjN18Y48mIon8f2Os6J+IFmMHN0IhNG/0PUIIsI07kA1sZ9t6vgZNBloVmcDa5WOSe6A==
"@microsoft/fast-element@^0.19.1":
version "0.19.1"
resolved "https://registry.yarnpkg.com/@microsoft/fast-element/-/fast-element-0.19.1.tgz#7b7cddc844168eb168f75e41ea63d23d942667cb"
integrity sha512-qfEqj4s7E3B0qsNRoiQyKF682fdpEbCINV/GS6tE/ZOFC/B0CKpcZjk3mWyw8XAfsLA50E4YuR9Hn8SuMc2sFg==
"@mrmlnc/readdir-enhanced@^2.2.1": "@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -3141,6 +3160,17 @@
resolved "https://registry.yarnpkg.com/@types/babel__code-frame/-/babel__code-frame-7.0.2.tgz#e0c0f1648cbc09a9d4e5b4ed2ae9a6f7c8f5aeb0" resolved "https://registry.yarnpkg.com/@types/babel__code-frame/-/babel__code-frame-7.0.2.tgz#e0c0f1648cbc09a9d4e5b4ed2ae9a6f7c8f5aeb0"
integrity sha512-imO+jT/yjOKOAS5GQZ8SDtwiIloAGGr6OaZDKB0V5JVaSfGZLat5K5/ZRtyKW6R60XHV3RHYPTFfhYb+wDKyKg== integrity sha512-imO+jT/yjOKOAS5GQZ8SDtwiIloAGGr6OaZDKB0V5JVaSfGZLat5K5/ZRtyKW6R60XHV3RHYPTFfhYb+wDKyKg==
"@types/babel__core@^7.1.12":
version "7.1.12"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d"
integrity sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==
dependencies:
"@babel/parser" "^7.1.0"
"@babel/types" "^7.0.0"
"@types/babel__generator" "*"
"@types/babel__template" "*"
"@types/babel__traverse" "*"
"@types/babel__core@^7.1.3": "@types/babel__core@^7.1.3":
version "7.1.10" version "7.1.10"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40"
@@ -3152,13 +3182,20 @@
"@types/babel__template" "*" "@types/babel__template" "*"
"@types/babel__traverse" "*" "@types/babel__traverse" "*"
"@types/babel__generator@*": "@types/babel__generator@*", "@types/babel__generator@^7.6.2":
version "7.6.2" version "7.6.2"
resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8"
integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==
dependencies: dependencies:
"@babel/types" "^7.0.0" "@babel/types" "^7.0.0"
"@types/babel__parser@^7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@types/babel__parser/-/babel__parser-7.1.1.tgz#39b311e54697636b0b98127700b5f01f5e1e85f4"
integrity sha512-baSzIb0QQOUQSglfR9gwXVSbHH91YvY00C9Zjq6E7sPdnp8oyPyUsonIj3SF4wUl0s96vR/kyWeVv30gmM/xZw==
dependencies:
"@babel/parser" "*"
"@types/babel__template@*": "@types/babel__template@*":
version "7.0.3" version "7.0.3"
resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.3.tgz#b8aaeba0a45caca7b56a5de9459872dde3727214" resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.3.tgz#b8aaeba0a45caca7b56a5de9459872dde3727214"
@@ -3167,7 +3204,7 @@
"@babel/parser" "^7.1.0" "@babel/parser" "^7.1.0"
"@babel/types" "^7.0.0" "@babel/types" "^7.0.0"
"@types/babel__traverse@*": "@types/babel__traverse@*", "@types/babel__traverse@^7.0.15":
version "7.0.15" version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03"
integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A== integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==
@@ -3627,6 +3664,11 @@
resolved "https://registry.yarnpkg.com/@types/path-is-inside/-/path-is-inside-1.0.0.tgz#02d6ff38975d684bdec96204494baf9f29f0e17f" resolved "https://registry.yarnpkg.com/@types/path-is-inside/-/path-is-inside-1.0.0.tgz#02d6ff38975d684bdec96204494baf9f29f0e17f"
integrity sha512-hfnXRGugz+McgX2jxyy5qz9sB21LRzlGn24zlwN2KEgoPtEvjzNRrLtUkOOebPDPZl3Rq7ywKxYvylVcEZDnEw== integrity sha512-hfnXRGugz+McgX2jxyy5qz9sB21LRzlGn24zlwN2KEgoPtEvjzNRrLtUkOOebPDPZl3Rq7ywKxYvylVcEZDnEw==
"@types/picomatch@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.2.1.tgz#f9e5a5e6ad03996832975ab7eadfa35791ca2a8f"
integrity sha512-26/tQcDmJXYHiaWAAIjnTVL5nwrT+IVaqFZIbBImAuKk/r/j1r/1hmZ7uaOzG6IknqP3QHcNNQ6QO8Vp28lUoA==
"@types/prop-types@*": "@types/prop-types@*":
version "15.7.3" version "15.7.3"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
@@ -3896,6 +3938,11 @@
resolved "https://registry.yarnpkg.com/@ungap/from-entries/-/from-entries-0.2.1.tgz#7e86196b8b2e99d73106a8f25c2a068326346354" resolved "https://registry.yarnpkg.com/@ungap/from-entries/-/from-entries-0.2.1.tgz#7e86196b8b2e99d73106a8f25c2a068326346354"
integrity sha512-CAqefTFAfnUPwYqsWHXpOxHaq1Zo5UQ3m9Zm2p09LggGe57rqHoBn3c++xcoomzXKynAUuiBMDUCQvKMnXjUpA== integrity sha512-CAqefTFAfnUPwYqsWHXpOxHaq1Zo5UQ3m9Zm2p09LggGe57rqHoBn3c++xcoomzXKynAUuiBMDUCQvKMnXjUpA==
"@ungap/promise-all-settled@1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
"@vue/compiler-core@3.0.2": "@vue/compiler-core@3.0.2":
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.2.tgz#7790b7a1fcbba5ace4d81a70ce59096fa5c95734" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.2.tgz#7790b7a1fcbba5ace4d81a70ce59096fa5c95734"
@@ -4023,6 +4070,29 @@
picomatch "^2.2.2" picomatch "^2.2.2"
ws "^7.3.1" ws "^7.3.1"
"@web/dev-server-core@^0.2.17", "@web/dev-server-core@^0.2.18":
version "0.2.18"
resolved "https://registry.yarnpkg.com/@web/dev-server-core/-/dev-server-core-0.2.18.tgz#c96bd373a9aa10429eceb13387202fd5ded06f4d"
integrity sha512-cgZuB0eG6i1wYZydVEd+g5J8Qa0EyRcZcPk3977+E298kOZ/2XPpM1tattDBQHu9J1afl9gX9bW80OH0iuTMhQ==
dependencies:
"@types/koa" "^2.11.6"
"@types/ws" "^7.2.6"
"@web/parse5-utils" "^1.0.0"
chokidar "^3.4.3"
clone "^2.1.2"
es-module-lexer "^0.3.26"
get-stream "^6.0.0"
is-stream "^2.0.0"
isbinaryfile "^4.0.6"
koa "^2.13.0"
koa-etag "^3.0.0"
koa-static "^5.0.0"
lru-cache "^5.1.1"
mime-types "^2.1.27"
parse5 "^6.0.1"
picomatch "^2.2.2"
ws "^7.3.1"
"@web/dev-server-esbuild@^0.2.4": "@web/dev-server-esbuild@^0.2.4":
version "0.2.6" version "0.2.6"
resolved "https://registry.yarnpkg.com/@web/dev-server-esbuild/-/dev-server-esbuild-0.2.6.tgz#b9a73a0bcbe6bb6ede4e3409b8e87ac781c92d5d" resolved "https://registry.yarnpkg.com/@web/dev-server-esbuild/-/dev-server-esbuild-0.2.6.tgz#b9a73a0bcbe6bb6ede4e3409b8e87ac781c92d5d"
@@ -4034,15 +4104,33 @@
parse5 "^6.0.1" parse5 "^6.0.1"
ua-parser-js "^0.7.21" ua-parser-js "^0.7.21"
"@web/dev-server-rollup@^0.2.10": "@web/dev-server-esbuild@^0.2.8":
version "0.2.10" version "0.2.8"
resolved "https://registry.yarnpkg.com/@web/dev-server-rollup/-/dev-server-rollup-0.2.10.tgz#d0e902b997f9cc445ec80fc2db5e93e27ffbdcae" resolved "https://registry.yarnpkg.com/@web/dev-server-esbuild/-/dev-server-esbuild-0.2.8.tgz#1ce7f83c670f36e0e98ee2ac88ba1fec53f8fae1"
integrity sha512-jJHxHiircfj7SngFmwa8Rlc2n4o2MB+mLY8y4JyIXJG00ifgxNU6kSjbV/5shPAzzN6MD/H60EkE7n7kYfuAig== integrity sha512-7HNIcoUVgVI7L/XGx/cSPGUqsIok41ucgxfIB+JYCAg+R0M9Jt8N7WQMfQFlHpoDLmqNpXuH+MYbY393u6hd6Q==
dependencies: dependencies:
"@web/dev-server-core" "^0.2.11" "@mdn/browser-compat-data" "^2.0.7"
"@web/dev-server-core" "^0.2.2"
esbuild "^0.8.12"
parse5 "^6.0.1"
ua-parser-js "^0.7.22"
"@web/dev-server-hmr@^0.1.6":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@web/dev-server-hmr/-/dev-server-hmr-0.1.6.tgz#5a8e8b2c02dcdf9392d001b11cb010acd4f92ce0"
integrity sha512-J3wQ46OeWMA5I48fyPNygbgbCLUIxvysAOanXMCVkuYen3ybWQ+T60CKqwYXGEOaRK3aWqxiyNFyHHvJzjeSIA==
dependencies:
"@web/dev-server-core" "^0.2.18"
"@web/dev-server-rollup@^0.2.11":
version "0.2.11"
resolved "https://registry.yarnpkg.com/@web/dev-server-rollup/-/dev-server-rollup-0.2.11.tgz#daa65b9e2f0eaeaa9d7c6d39d0943d494eda8bb7"
integrity sha512-cqeXC93BElaKIV0Bel05M86Or5HQKem7FuBjfoMMlgp0irt6ZYCovYe7l2rG0Hw+/MCYaNs+uxjMK5UYLuWMBw==
dependencies:
"@web/dev-server-core" "^0.2.17"
chalk "^4.1.0" chalk "^4.1.0"
parse5 "^6.0.1" parse5 "^6.0.1"
rollup "^2.20.0" rollup "^2.33.2"
whatwg-url "^8.1.0" whatwg-url "^8.1.0"
"@web/dev-server-rollup@^0.2.5", "@web/dev-server-rollup@^0.2.9": "@web/dev-server-rollup@^0.2.5", "@web/dev-server-rollup@^0.2.9":
@@ -4103,27 +4191,26 @@
open "^7.1.0" open "^7.1.0"
portfinder "^1.0.27" portfinder "^1.0.27"
"@web/dev-server@^0.0.19": "@web/dev-server@^0.0.24":
version "0.0.19" version "0.0.24"
resolved "https://registry.yarnpkg.com/@web/dev-server/-/dev-server-0.0.19.tgz#cf473079269fa2fdd6eee728f7a67a0648ec5e30" resolved "https://registry.yarnpkg.com/@web/dev-server/-/dev-server-0.0.24.tgz#891de7da5497a538d74b50b7c99a492a1f5661b3"
integrity sha512-e+mFEaXB/Fax8OcFT9+b/Z/4NqG/qr6edITgQdk3WxW5O+HYF0ramsZ6grSCwS9tly+/QfTlCnLJCMPOcibqYA== integrity sha512-vBvI6Q3h7oiTq2G9j6Xlj4LkCpTulSycEDvaFCrHwadDvKLoQLPFqm1wv4A8mvjU0zm6Mjs5X5eZndNJU0naIA==
dependencies: dependencies:
"@babel/code-frame" "^7.10.4" "@babel/code-frame" "^7.10.4"
"@rollup/plugin-node-resolve" "^8.4.0" "@rollup/plugin-node-resolve" "^8.4.0"
"@types/command-line-args" "^5.0.0" "@types/command-line-args" "^5.0.0"
"@web/config-loader" "^0.1.1" "@web/config-loader" "^0.1.1"
"@web/dev-server-cli" "^0.0.3" "@web/dev-server-core" "^0.2.18"
"@web/dev-server-core" "^0.2.15" "@web/dev-server-rollup" "^0.2.11"
"@web/dev-server-rollup" "^0.2.10" camelcase "^6.2.0"
camelcase "^6.0.0"
chalk "^4.1.0" chalk "^4.1.0"
command-line-args "^5.1.1" command-line-args "^5.1.1"
command-line-usage "^6.1.0" command-line-usage "^6.1.1"
debounce "^1.2.0" debounce "^1.2.0"
deepmerge "^4.2.2" deepmerge "^4.2.2"
ip "^1.1.5" ip "^1.1.5"
open "^7.1.0" open "^7.3.0"
portfinder "^1.0.27" portfinder "^1.0.28"
"@web/parse5-utils@^1.0.0", "@web/parse5-utils@^1.1.2": "@web/parse5-utils@^1.0.0", "@web/parse5-utils@^1.1.2":
version "1.1.2" version "1.1.2"
@@ -4662,16 +4749,16 @@ ansi-colors@3.2.3:
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813"
integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==
ansi-colors@4.1.1, ansi-colors@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
ansi-colors@^3.0.0: ansi-colors@^3.0.0:
version "3.2.4" version "3.2.4"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
ansi-colors@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: ansi-escapes@^3.1.0, ansi-escapes@^3.2.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
@@ -6310,6 +6397,11 @@ camelcase@^6.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.1.0.tgz#27dc176173725fb0adf8a48b647f4d7871944d78" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.1.0.tgz#27dc176173725fb0adf8a48b647f4d7871944d78"
integrity sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ== integrity sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ==
camelcase@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
can-use-dom@^0.1.0: can-use-dom@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/can-use-dom/-/can-use-dom-0.1.0.tgz#22cc4a34a0abc43950f42c6411024a3f6366b45a" resolved "https://registry.yarnpkg.com/can-use-dom/-/can-use-dom-0.1.0.tgz#22cc4a34a0abc43950f42c6411024a3f6366b45a"
@@ -6527,6 +6619,21 @@ cheerio@^1.0.0-rc.3:
lodash "^4.15.0" lodash "^4.15.0"
parse5 "^3.0.1" parse5 "^3.0.1"
chokidar@3.4.3, chokidar@^3.0.0, chokidar@^3.0.2, chokidar@^3.3.0, chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
dependencies:
anymatch "~3.1.1"
braces "~3.0.2"
glob-parent "~5.1.0"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.5.0"
optionalDependencies:
fsevents "~2.1.2"
chokidar@^2.0.4, chokidar@^2.1.8: chokidar@^2.0.4, chokidar@^2.1.8:
version "2.1.8" version "2.1.8"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
@@ -6546,21 +6653,6 @@ chokidar@^2.0.4, chokidar@^2.1.8:
optionalDependencies: optionalDependencies:
fsevents "^1.2.7" fsevents "^1.2.7"
chokidar@^3.0.0, chokidar@^3.0.2, chokidar@^3.3.0, chokidar@^3.4.0, chokidar@^3.4.1:
version "3.4.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
dependencies:
anymatch "~3.1.1"
braces "~3.0.2"
glob-parent "~5.1.0"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.5.0"
optionalDependencies:
fsevents "~2.1.2"
chownr@^1.1.1, chownr@^1.1.2: chownr@^1.1.1, chownr@^1.1.2:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
@@ -6960,6 +7052,16 @@ command-line-usage@^6.1.0:
table-layout "^1.0.0" table-layout "^1.0.0"
typical "^5.2.0" typical "^5.2.0"
command-line-usage@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.1.tgz#c908e28686108917758a49f45efb4f02f76bc03f"
integrity sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA==
dependencies:
array-back "^4.0.1"
chalk "^2.4.2"
table-layout "^1.0.1"
typical "^5.2.0"
commander@^2.19.0, commander@^2.2.0, commander@^2.20.0, commander@^2.20.3, commander@^2.3.0, commander@^2.8.1: commander@^2.19.0, commander@^2.2.0, commander@^2.20.0, commander@^2.20.3, commander@^2.3.0, commander@^2.8.1:
version "2.20.3" version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -7670,7 +7772,7 @@ debug@3.2.6, debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: debug@4, debug@4.2.0, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
@@ -7704,6 +7806,11 @@ decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
decamelize@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
decimal.js@^10.2.0: decimal.js@^10.2.0:
version "10.2.1" version "10.2.1"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3"
@@ -8102,7 +8209,7 @@ diff@3.5.0, diff@^3.5.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
diff@^4.0.2: diff@4.0.2, diff@^4.0.2:
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
@@ -8761,6 +8868,11 @@ es-module-lexer@^0.3.13, es-module-lexer@^0.3.24, es-module-lexer@^0.3.25:
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.3.25.tgz#24a1abcb9c5dc96923a8e42be033b801f788de06" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.3.25.tgz#24a1abcb9c5dc96923a8e42be033b801f788de06"
integrity sha512-H9VoFD5H9zEfiOX2LeTWDwMvAbLqcAyA2PIb40TOAvGpScOjit02oTGWgIh+M0rx2eJOKyJVM9wtpKFVgnyC3A== integrity sha512-H9VoFD5H9zEfiOX2LeTWDwMvAbLqcAyA2PIb40TOAvGpScOjit02oTGWgIh+M0rx2eJOKyJVM9wtpKFVgnyC3A==
es-module-lexer@^0.3.26:
version "0.3.26"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.3.26.tgz#7b507044e97d5b03b01d4392c74ffeb9c177a83b"
integrity sha512-Va0Q/xqtrss45hWzP8CZJwzGSZJjDM5/MJRE3IXXnUCcVLElR9BRaE9F62BopysASyc4nM3uwhSW7FFB9nlWAA==
es-module-shims@^0.4.6: es-module-shims@^0.4.6:
version "0.4.7" version "0.4.7"
resolved "https://registry.yarnpkg.com/es-module-shims/-/es-module-shims-0.4.7.tgz#1419b65bbd38dfe91ab8ea5d7b4b454561e44641" resolved "https://registry.yarnpkg.com/es-module-shims/-/es-module-shims-0.4.7.tgz#1419b65bbd38dfe91ab8ea5d7b4b454561e44641"
@@ -8790,6 +8902,11 @@ esbuild@^0.7.15:
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.7.22.tgz#9149b903f8128b7c45a754046c24199d76bbe08e" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.7.22.tgz#9149b903f8128b7c45a754046c24199d76bbe08e"
integrity sha512-B43SYg8LGWYTCv9Gs0RnuLNwjzpuWOoCaZHTWEDEf5AfrnuDMerPVMdCEu7xOdhFvQ+UqfP2MGU9lxEy0JzccA== integrity sha512-B43SYg8LGWYTCv9Gs0RnuLNwjzpuWOoCaZHTWEDEf5AfrnuDMerPVMdCEu7xOdhFvQ+UqfP2MGU9lxEy0JzccA==
esbuild@^0.8.12:
version "0.8.12"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.12.tgz#bea417dbb228a971a5df255cb1b8acf5ea96ef52"
integrity sha512-SWCUodYFOkKcu2Jbum12NbD98MoQq2EWWvHfQtpR2C9RkPzRr1BE4cavYjQpQV01hBR19UAftXl0TVLjka/i3Q==
escalade@^3.1.0, escalade@^3.1.1: escalade@^3.1.0, escalade@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -9614,6 +9731,14 @@ find-up@3.0.0, find-up@^3.0.0:
dependencies: dependencies:
locate-path "^3.0.0" locate-path "^3.0.0"
find-up@5.0.0, find-up@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
dependencies:
locate-path "^6.0.0"
path-exists "^4.0.0"
find-up@^1.0.0: find-up@^1.0.0:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@@ -9637,14 +9762,6 @@ find-up@^4.0.0, find-up@^4.1.0:
locate-path "^5.0.0" locate-path "^5.0.0"
path-exists "^4.0.0" path-exists "^4.0.0"
find-up@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
dependencies:
locate-path "^6.0.0"
path-exists "^4.0.0"
find-versions@^3.0.0: find-versions@^3.0.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e"
@@ -9676,6 +9793,11 @@ flat@^4.1.0:
dependencies: dependencies:
is-buffer "~2.0.3" is-buffer "~2.0.3"
flat@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
flatted@^2.0.0: flatted@^2.0.0:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
@@ -10164,7 +10286,7 @@ glob@7.1.3:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.1.6" version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -12222,7 +12344,7 @@ js-yaml@3.13.1:
argparse "^1.0.7" argparse "^1.0.7"
esprima "^4.0.0" esprima "^4.0.0"
js-yaml@^3.11.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.14.0, js-yaml@^3.6.1: js-yaml@3.14.0, js-yaml@^3.11.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.14.0, js-yaml@^3.6.1:
version "3.14.0" version "3.14.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
@@ -12816,7 +12938,7 @@ listr2@^2.6.0:
rxjs "^6.6.2" rxjs "^6.6.2"
through "^2.3.8" through "^2.3.8"
lit-element@^2.0.1, lit-element@^2.2.1, lit-element@^2.3.1, lit-element@~2.4.0: lit-element@^2.0.1, lit-element@^2.2.1, lit-element@^2.3.1, lit-element@^2.4.0, lit-element@~2.4.0:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-2.4.0.tgz#b22607a037a8fc08f5a80736dddf7f3f5d401452" resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-2.4.0.tgz#b22607a037a8fc08f5a80736dddf7f3f5d401452"
integrity sha512-pBGLglxyhq/Prk2H91nA0KByq/hx/wssJBQFiYqXhGDvEnY31PRGYf1RglVzyLeRysu0IHm2K0P196uLLWmwFg== integrity sha512-pBGLglxyhq/Prk2H91nA0KByq/hx/wssJBQFiYqXhGDvEnY31PRGYf1RglVzyLeRysu0IHm2K0P196uLLWmwFg==
@@ -13137,6 +13259,13 @@ log-symbols@2.2.0:
dependencies: dependencies:
chalk "^2.0.1" chalk "^2.0.1"
log-symbols@4.0.0, log-symbols@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920"
integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==
dependencies:
chalk "^4.0.0"
log-symbols@^1.0.0: log-symbols@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
@@ -13151,13 +13280,6 @@ log-symbols@^3.0.0:
dependencies: dependencies:
chalk "^2.4.2" chalk "^2.4.2"
log-symbols@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920"
integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==
dependencies:
chalk "^4.0.0"
log-update@^4.0.0: log-update@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1"
@@ -13895,6 +14017,37 @@ mocha@^6.2.2:
yargs-parser "13.1.2" yargs-parser "13.1.2"
yargs-unparser "1.6.0" yargs-unparser "1.6.0"
mocha@^8.2.1:
version "8.2.1"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.2.1.tgz#f2fa68817ed0e53343d989df65ccd358bc3a4b39"
integrity sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==
dependencies:
"@ungap/promise-all-settled" "1.1.2"
ansi-colors "4.1.1"
browser-stdout "1.3.1"
chokidar "3.4.3"
debug "4.2.0"
diff "4.0.2"
escape-string-regexp "4.0.0"
find-up "5.0.0"
glob "7.1.6"
growl "1.10.5"
he "1.2.0"
js-yaml "3.14.0"
log-symbols "4.0.0"
minimatch "3.0.4"
ms "2.1.2"
nanoid "3.1.12"
serialize-javascript "5.0.1"
strip-json-comments "3.1.1"
supports-color "7.2.0"
which "2.0.2"
wide-align "1.1.3"
workerpool "6.0.2"
yargs "13.3.2"
yargs-parser "13.1.2"
yargs-unparser "2.0.0"
mock-require@^3.0.3: mock-require@^3.0.3:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/mock-require/-/mock-require-3.0.3.tgz#ccd544d9eae81dd576b3f219f69ec867318a1946" resolved "https://registry.yarnpkg.com/mock-require/-/mock-require-3.0.3.tgz#ccd544d9eae81dd576b3f219f69ec867318a1946"
@@ -14010,6 +14163,11 @@ nan@^2.12.1:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
nanoid@3.1.12:
version "3.1.12"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654"
integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==
nanomatch@^1.2.9: nanomatch@^1.2.9:
version "1.2.13" version "1.2.13"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@@ -14628,7 +14786,7 @@ open@^6.3.0:
dependencies: dependencies:
is-wsl "^1.1.0" is-wsl "^1.1.0"
open@^7.0.0, open@^7.0.3, open@^7.0.4, open@^7.1.0: open@^7.0.0, open@^7.0.3, open@^7.0.4, open@^7.1.0, open@^7.3.0:
version "7.3.0" version "7.3.0"
resolved "https://registry.yarnpkg.com/open/-/open-7.3.0.tgz#45461fdee46444f3645b6e14eb3ca94b82e1be69" resolved "https://registry.yarnpkg.com/open/-/open-7.3.0.tgz#45461fdee46444f3645b6e14eb3ca94b82e1be69"
integrity sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw== integrity sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==
@@ -15377,7 +15535,7 @@ popper.js@^1.14.4, popper.js@^1.14.7, popper.js@^1.15.0:
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
portfinder@^1.0.13, portfinder@^1.0.21, portfinder@^1.0.26, portfinder@^1.0.27: portfinder@^1.0.13, portfinder@^1.0.21, portfinder@^1.0.26, portfinder@^1.0.27, portfinder@^1.0.28:
version "1.0.28" version "1.0.28"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==
@@ -17361,6 +17519,13 @@ rollup@^2.33.1:
optionalDependencies: optionalDependencies:
fsevents "~2.1.2" fsevents "~2.1.2"
rollup@^2.33.2:
version "2.33.3"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.33.3.tgz#ae72ce31f992b09a580072951bfea76e9df17342"
integrity sha512-RpayhPTe4Gu/uFGCmk7Gp5Z9Qic2VsqZ040G+KZZvsZYdcuWaJg678JeDJJvJeEQXminu24a2au+y92CUWVd+w==
optionalDependencies:
fsevents "~2.1.2"
rsvp@^4.8.4: rsvp@^4.8.4:
version "4.8.5" version "4.8.5"
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
@@ -17645,6 +17810,13 @@ sentence-case@^3.0.3:
tslib "^1.10.0" tslib "^1.10.0"
upper-case-first "^2.0.1" upper-case-first "^2.0.1"
serialize-javascript@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
dependencies:
randombytes "^2.1.0"
serialize-javascript@^3.0.0: serialize-javascript@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea"
@@ -18613,7 +18785,7 @@ strip-json-comments@2.0.1, strip-json-comments@~2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
@@ -18654,6 +18826,13 @@ supports-color@6.0.0:
dependencies: dependencies:
has-flag "^3.0.0" has-flag "^3.0.0"
supports-color@7.2.0, supports-color@^7.0.0, supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
supports-color@^0.2.0: supports-color@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a"
@@ -18678,13 +18857,6 @@ supports-color@^6.1.0:
dependencies: dependencies:
has-flag "^3.0.0" has-flag "^3.0.0"
supports-color@^7.0.0, supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
supports-hyperlinks@^1.0.1: supports-hyperlinks@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz#71daedf36cc1060ac5100c351bb3da48c29c0ef7" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz#71daedf36cc1060ac5100c351bb3da48c29c0ef7"
@@ -18729,7 +18901,7 @@ systemjs@^6.3.1, systemjs@^6.7.1:
resolved "https://registry.yarnpkg.com/systemjs/-/systemjs-6.7.1.tgz#3db5036f450180a0701e078fbb5b434a690026f0" resolved "https://registry.yarnpkg.com/systemjs/-/systemjs-6.7.1.tgz#3db5036f450180a0701e078fbb5b434a690026f0"
integrity sha512-Q78H/SYy9ErC8PH8r9vA/FcQ3X+Hf33dOpx2JKP/Ma6f2gHuSScPFuCKZH+6CIj7EsIJlzODxSG4mMIpjOh5oA== integrity sha512-Q78H/SYy9ErC8PH8r9vA/FcQ3X+Hf33dOpx2JKP/Ma6f2gHuSScPFuCKZH+6CIj7EsIJlzODxSG4mMIpjOh5oA==
table-layout@^1.0.0: table-layout@^1.0.0, table-layout@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.1.tgz#8411181ee951278ad0638aea2f779a9ce42894f9" resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.1.tgz#8411181ee951278ad0638aea2f779a9ce42894f9"
integrity sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q== integrity sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q==
@@ -19384,7 +19556,7 @@ typical@^5.0.0, typical@^5.2.0:
resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066"
integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==
ua-parser-js@^0.7.18, ua-parser-js@^0.7.21: ua-parser-js@^0.7.18, ua-parser-js@^0.7.21, ua-parser-js@^0.7.22:
version "0.7.22" version "0.7.22"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.22.tgz#960df60a5f911ea8f1c818f3747b99c6e177eae3" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.22.tgz#960df60a5f911ea8f1c818f3747b99c6e177eae3"
integrity sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q== integrity sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q==
@@ -20255,7 +20427,7 @@ which@1.3.1, which@^1.2.9, which@^1.3.1:
dependencies: dependencies:
isexe "^2.0.0" isexe "^2.0.0"
which@^2.0.1, which@^2.0.2: which@2.0.2, which@^2.0.1, which@^2.0.2:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
@@ -20519,6 +20691,11 @@ worker-rpc@^0.1.0:
dependencies: dependencies:
microevent.ts "~0.1.1" microevent.ts "~0.1.1"
workerpool@6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.2.tgz#e241b43d8d033f1beb52c7851069456039d1d438"
integrity sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==
wrap-ansi@^4.0.0: wrap-ansi@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-4.0.0.tgz#b3570d7c70156159a2d42be5cc942e957f7b1131" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-4.0.0.tgz#b3570d7c70156159a2d42be5cc942e957f7b1131"
@@ -20746,6 +20923,16 @@ yargs-unparser@1.6.0:
lodash "^4.17.15" lodash "^4.17.15"
yargs "^13.3.0" yargs "^13.3.0"
yargs-unparser@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
dependencies:
camelcase "^6.0.0"
decamelize "^4.0.0"
flat "^5.0.2"
is-plain-obj "^2.1.0"
yargs@13.3.0: yargs@13.3.0:
version "13.3.0" version "13.3.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83"