Socket
Socket
Sign inDemoInstall

@vue/web-component-wrapper

Package Overview
Dependencies
0
Maintainers
8
Versions
8
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.4 to 1.2.0

173

dist/vue-wc-wrapper.global.js

@@ -22,2 +22,7 @@ var wrapVueWebComponent = (function () {

function injectHook (options, key, hook) {
options[key] = [].concat(options[key] || []);
options[key].unshift(hook);
}
function callHooks (vm, hook) {

@@ -98,36 +103,75 @@ if (vm) {

function wrap (Vue, Component) {
const options = typeof Component === 'function'
? Component.options
: Component;
const isAsync = typeof Component === 'function' && !Component.cid;
let isInitialized = false;
let hyphenatedPropsList;
let camelizedPropsList;
let camelizedPropsMap;
// inject hook to proxy $emit to native DOM events
options.beforeCreate = [].concat(options.beforeCreate || []);
options.beforeCreate.unshift(function () {
const emit = this.$emit;
this.$emit = (name, ...args) => {
this.$root.$options.customElement.dispatchEvent(createCustomEvent(name, args));
return emit.call(this, name, ...args)
};
});
function initialize (Component) {
if (isInitialized) return
// extract props info
const propsList = Array.isArray(options.props)
? options.props
: Object.keys(options.props || {});
const hyphenatedPropsList = propsList.map(hyphenate);
const camelizedPropsList = propsList.map(camelize);
const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {};
const camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
map[key] = originalPropsAsObject[propsList[i]];
return map
}, {});
const options = typeof Component === 'function'
? Component.options
: Component;
// extract props info
const propsList = Array.isArray(options.props)
? options.props
: Object.keys(options.props || {});
hyphenatedPropsList = propsList.map(hyphenate);
camelizedPropsList = propsList.map(camelize);
const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {};
camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
map[key] = originalPropsAsObject[propsList[i]];
return map
}, {});
// proxy $emit to native DOM events
injectHook(options, 'beforeCreate', function () {
const emit = this.$emit;
this.$emit = (name, ...args) => {
this.$root.$options.customElement.dispatchEvent(createCustomEvent(name, args));
return emit.call(this, name, ...args)
};
});
injectHook(options, 'created', function () {
// sync default props values to wrapper on created
camelizedPropsList.forEach(key => {
this.$root.props[key] = this[key];
});
});
// proxy props as Element properties
camelizedPropsList.forEach(key => {
Object.defineProperty(CustomElement.prototype, key, {
get () {
return this._wrapper.props[key]
},
set (newVal) {
this._wrapper.props[key] = newVal;
},
enumerable: false,
configurable: true
});
});
isInitialized = true;
}
function syncAttribute (el, key) {
const camelized = camelize(key);
const value = el.hasAttribute(key) ? el.getAttribute(key) : undefined;
el._wrapper.props[camelized] = convertAttributeValue(
value,
key,
camelizedPropsMap[camelized]
);
}
class CustomElement extends HTMLElement {
static get observedAttributes () {
return hyphenatedPropsList
}
constructor () {
super();
this.attachShadow({ mode: 'open' });
const wrapper = this._wrapper = new Vue({

@@ -139,3 +183,3 @@ name: 'shadow-root',

return {
props: getInitialProps(camelizedPropsList),
props: {},
slotChildren: []

@@ -152,8 +196,19 @@ }

// Use MutationObserver to react to slot content change
const observer = new MutationObserver(() => {
wrapper.slotChildren = Object.freeze(toVNodes(
wrapper.$createElement,
this.childNodes
));
// Use MutationObserver to react to future attribute & slot content change
const observer = new MutationObserver(mutations => {
let hasChildrenChange = false;
for (let i = 0; i < mutations.length; i++) {
const m = mutations[i];
if (isInitialized && m.type === 'attributes' && m.target === this) {
syncAttribute(this, m.attributeName);
} else {
hasChildrenChange = true;
}
}
if (hasChildrenChange) {
wrapper.slotChildren = Object.freeze(toVNodes(
wrapper.$createElement,
this.childNodes
));
}
});

@@ -175,2 +230,22 @@ observer.observe(this, {

if (!wrapper._isMounted) {
// initialize attributes
const syncInitialAttributes = () => {
wrapper.props = getInitialProps(camelizedPropsList);
hyphenatedPropsList.forEach(key => {
syncAttribute(this, key);
});
};
if (isInitialized) {
syncInitialAttributes();
} else {
// async & unresolved
Component().then(resolved => {
if (resolved.__esModule || resolved[Symbol.toStringTag] === 'Module') {
resolved = resolved.default;
}
initialize(resolved);
syncInitialAttributes();
});
}
// initialize children

@@ -182,6 +257,2 @@ wrapper.slotChildren = Object.freeze(toVNodes(

wrapper.$mount();
// sync default props values to wrapper
camelizedPropsList.forEach(key => {
wrapper.props[key] = this.vueComponent[key];
});
this.shadowRoot.appendChild(wrapper.$el);

@@ -196,28 +267,8 @@ } else {

}
}
// watch attribute change and sync
attributeChangedCallback (attrName, oldVal, newVal) {
const camelized = camelize(attrName);
this._wrapper.props[camelized] = convertAttributeValue(
newVal,
attrName,
camelizedPropsMap[camelized]
);
}
if (!isAsync) {
initialize(Component);
}
// proxy props as Element properties
camelizedPropsList.forEach(key => {
Object.defineProperty(CustomElement.prototype, key, {
get () {
return this._wrapper.props[key]
},
set (newVal) {
this._wrapper.props[key] = newVal;
},
enumerable: false,
configurable: true
});
});
return CustomElement

@@ -224,0 +275,0 @@ }

@@ -19,2 +19,7 @@ const camelizeRE = /-(\w)/g;

function injectHook (options, key, hook) {
options[key] = [].concat(options[key] || []);
options[key].unshift(hook);
}
function callHooks (vm, hook) {

@@ -95,36 +100,75 @@ if (vm) {

function wrap (Vue, Component) {
const options = typeof Component === 'function'
? Component.options
: Component;
const isAsync = typeof Component === 'function' && !Component.cid;
let isInitialized = false;
let hyphenatedPropsList;
let camelizedPropsList;
let camelizedPropsMap;
// inject hook to proxy $emit to native DOM events
options.beforeCreate = [].concat(options.beforeCreate || []);
options.beforeCreate.unshift(function () {
const emit = this.$emit;
this.$emit = (name, ...args) => {
this.$root.$options.customElement.dispatchEvent(createCustomEvent(name, args));
return emit.call(this, name, ...args)
};
});
function initialize (Component) {
if (isInitialized) return
// extract props info
const propsList = Array.isArray(options.props)
? options.props
: Object.keys(options.props || {});
const hyphenatedPropsList = propsList.map(hyphenate);
const camelizedPropsList = propsList.map(camelize);
const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {};
const camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
map[key] = originalPropsAsObject[propsList[i]];
return map
}, {});
const options = typeof Component === 'function'
? Component.options
: Component;
// extract props info
const propsList = Array.isArray(options.props)
? options.props
: Object.keys(options.props || {});
hyphenatedPropsList = propsList.map(hyphenate);
camelizedPropsList = propsList.map(camelize);
const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {};
camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
map[key] = originalPropsAsObject[propsList[i]];
return map
}, {});
// proxy $emit to native DOM events
injectHook(options, 'beforeCreate', function () {
const emit = this.$emit;
this.$emit = (name, ...args) => {
this.$root.$options.customElement.dispatchEvent(createCustomEvent(name, args));
return emit.call(this, name, ...args)
};
});
injectHook(options, 'created', function () {
// sync default props values to wrapper on created
camelizedPropsList.forEach(key => {
this.$root.props[key] = this[key];
});
});
// proxy props as Element properties
camelizedPropsList.forEach(key => {
Object.defineProperty(CustomElement.prototype, key, {
get () {
return this._wrapper.props[key]
},
set (newVal) {
this._wrapper.props[key] = newVal;
},
enumerable: false,
configurable: true
});
});
isInitialized = true;
}
function syncAttribute (el, key) {
const camelized = camelize(key);
const value = el.hasAttribute(key) ? el.getAttribute(key) : undefined;
el._wrapper.props[camelized] = convertAttributeValue(
value,
key,
camelizedPropsMap[camelized]
);
}
class CustomElement extends HTMLElement {
static get observedAttributes () {
return hyphenatedPropsList
}
constructor () {
super();
this.attachShadow({ mode: 'open' });
const wrapper = this._wrapper = new Vue({

@@ -136,3 +180,3 @@ name: 'shadow-root',

return {
props: getInitialProps(camelizedPropsList),
props: {},
slotChildren: []

@@ -149,8 +193,19 @@ }

// Use MutationObserver to react to slot content change
const observer = new MutationObserver(() => {
wrapper.slotChildren = Object.freeze(toVNodes(
wrapper.$createElement,
this.childNodes
));
// Use MutationObserver to react to future attribute & slot content change
const observer = new MutationObserver(mutations => {
let hasChildrenChange = false;
for (let i = 0; i < mutations.length; i++) {
const m = mutations[i];
if (isInitialized && m.type === 'attributes' && m.target === this) {
syncAttribute(this, m.attributeName);
} else {
hasChildrenChange = true;
}
}
if (hasChildrenChange) {
wrapper.slotChildren = Object.freeze(toVNodes(
wrapper.$createElement,
this.childNodes
));
}
});

@@ -172,2 +227,22 @@ observer.observe(this, {

if (!wrapper._isMounted) {
// initialize attributes
const syncInitialAttributes = () => {
wrapper.props = getInitialProps(camelizedPropsList);
hyphenatedPropsList.forEach(key => {
syncAttribute(this, key);
});
};
if (isInitialized) {
syncInitialAttributes();
} else {
// async & unresolved
Component().then(resolved => {
if (resolved.__esModule || resolved[Symbol.toStringTag] === 'Module') {
resolved = resolved.default;
}
initialize(resolved);
syncInitialAttributes();
});
}
// initialize children

@@ -179,6 +254,2 @@ wrapper.slotChildren = Object.freeze(toVNodes(

wrapper.$mount();
// sync default props values to wrapper
camelizedPropsList.forEach(key => {
wrapper.props[key] = this.vueComponent[key];
});
this.shadowRoot.appendChild(wrapper.$el);

@@ -193,28 +264,8 @@ } else {

}
}
// watch attribute change and sync
attributeChangedCallback (attrName, oldVal, newVal) {
const camelized = camelize(attrName);
this._wrapper.props[camelized] = convertAttributeValue(
newVal,
attrName,
camelizedPropsMap[camelized]
);
}
if (!isAsync) {
initialize(Component);
}
// proxy props as Element properties
camelizedPropsList.forEach(key => {
Object.defineProperty(CustomElement.prototype, key, {
get () {
return this._wrapper.props[key]
},
set (newVal) {
this._wrapper.props[key] = newVal;
},
enumerable: false,
configurable: true
});
});
return CustomElement

@@ -221,0 +272,0 @@ }

{
"name": "@vue/web-component-wrapper",
"version": "1.1.4",
"version": "1.2.0",
"description": "wrap a vue component as a web component.",

@@ -5,0 +5,0 @@ "main": "dist/vue-wc-wrapper.js",

@@ -42,2 +42,10 @@ # @vue/web-component-wrapper [![CircleCI](https://circleci.com/gh/vuejs/vue-web-component-wrapper.svg?style=shield)](https://circleci.com/gh/vuejs/vue-web-component-wrapper)

Note it works with async components as well - the async component factory will only be called when an instance of the custom element is created on the page:
``` js
const CustomElement = wrap(Vue, () => import(`MyComponent.vue`))
window.customElements.define('my-element', CustomElement)
```
## Interface Proxying Details

@@ -44,0 +52,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc