Browse Source

[frontend] Publishing web component wrapper as an NPM package - vue3-webcomponent-wrapper

sreenaths 4 years ago
parent
commit
70d8aac539

+ 2 - 2
desktop/core/src/desktop/js/vue/webComponentWrap.ts

@@ -14,7 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import { ComponentOptions, Component } from 'vue';
+import { ComponentOptions, Component, createApp, h } from 'vue';
 import wrapper, { WebComponentOptions } from './wrapper/index';
 
 export interface HueComponentOptions<T extends Component> extends ComponentOptions<T> {
@@ -31,7 +31,7 @@ export const wrap = <T extends Component>(
   options?: WebComponentOptions
 ): void => {
   if (!isRegistered(tag)) {
-    const customElement: CustomElementConstructor = wrapper(component, options);
+    const customElement: CustomElementConstructor = wrapper(component, createApp, h, options);
     window.customElements.define(tag, customElement);
   }
 };

+ 5 - 3
desktop/core/src/desktop/js/vue/wrapper/index.ts

@@ -21,8 +21,8 @@
 
 import {
   Component,
-  h,
-  createApp,
+  CreateAppFunction,
+  ConcreteComponent,
   App,
   ComponentPublicInstance,
   VNode,
@@ -51,6 +51,8 @@ export interface WebComponentOptions {
  */
 export default function wrap(
   component: Component,
+  createApp: CreateAppFunction<Element>,
+  h: <P>(type: ConcreteComponent<P> | string, props?: KeyHash, children?: () => unknown) => VNode,
   options?: WebComponentOptions
 ): CustomElementConstructor {
   const componentObj: ComponentOptionsWithObjectProps = <ComponentOptionsWithObjectProps>component;
@@ -176,7 +178,7 @@ export default function wrap(
     }
 
     syncSlots(): void {
-      this._slotChildren = toVNodes(this.childNodes);
+      this._slotChildren = toVNodes(this.childNodes, h);
       this._component?.$forceUpdate();
     }
 

+ 8 - 4
desktop/core/src/desktop/js/vue/wrapper/utils.ts

@@ -1,4 +1,4 @@
-import { ComponentPublicInstance, h, VNode } from 'vue';
+import { ComponentPublicInstance, VNode } from 'vue';
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 export type KeyHash = { [key: string]: any };
@@ -65,18 +65,22 @@ export function convertAttributeValue(
   }
 }
 
-export function toVNodes(children: NodeListOf<ChildNode>): (VNode | null)[] {
+export function toVNodes(
+  children: NodeListOf<ChildNode>,
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  h: (name: string, data: any) => VNode
+): (VNode | null)[] {
   const res: (VNode | null)[] = [];
 
   for (let i = 0, l = children.length; i < l; i++) {
-    res.push(toVNode(children[i]));
+    res.push(toVNode(children[i], h));
   }
 
   return res;
 }
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
-function toVNode(node: any): VNode | null {
+function toVNode(node: any, h: (name: string, data: any) => VNode): VNode | null {
   if (node.nodeType === 3) {
     return node.data.trim() ? node.data : null;
   } else if (node.nodeType === 1) {

+ 2 - 0
tools/vue3-webcomponent-wrapper/.gitignore

@@ -0,0 +1,2 @@
+src/
+dist/

+ 27 - 0
tools/vue3-webcomponent-wrapper/README.md

@@ -0,0 +1,27 @@
+# vue3-webcomponent-wrapper
+
+Vue 3 wrapper to convert a Vue component into Web Component. It supports reactive attributes, events & slots.
+
+This is a port of [@vuejs/vue-web-component-wrapper](https://github.com/vuejs/vue-web-component-wrapper) (Official Vue 2 web component wrapper package) to work with Vue 3. And must be deprecated once the official wrapper package  starts supporting Vue 3. Progress of Vue3 support is tracked in this [issue.](https://github.com/vuejs/vue-web-component-wrapper/issues/93)
+
+One main blocker preventing the official wrapper from upgrading was the lack of shadow-root CSS injection in Vue 3 build tooling. As we could live without a shadow dom in Hue this was not an issue and this port was created.
+
+## Usage
+
+    import { createApp, h } from "vue";
+    import wrapper from "vue3-webcomponent-wrapper";
+    import MyComponent from "./components/MyComponent.vue";
+
+    const webComponent = wrapper(MyComponent, createApp, h);
+    window.customElements.define("my-component", webComponent);
+
+
+Please find more information in this [demo app](https://github.com/sreenaths/vue3-webcomponent-wrapper-demo).
+
+## Build & Publish
+
+vue3-webcomponent-wrapper is build as part of gethue npm build. Following are the steps to be followed under hue root to build and publish this package.
+
+    npm run webpack-npm
+    cd tools/vue3-webcomponent-wrapper
+    npm publish

+ 26 - 0
tools/vue3-webcomponent-wrapper/package.json

@@ -0,0 +1,26 @@
+{
+  "name": "vue3-webcomponent-wrapper",
+  "version": "0.1.2",
+  "description": "Wrap and register a Vue 3 component as a custom element.",
+  "main": "dist/index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/cloudera/hue.git"
+  },
+  "keywords": [
+    "Vue3",
+    "Web",
+    "Component",
+    "Hue"
+  ],
+  "author": "Sreenath Somarajapuram <ssomarajapuram@cloudera.com>",
+  "license": "Apache-2.0",
+  "bugs": {
+    "url": "https://github.com/cloudera/hue/issues"
+  },
+  "homepage": "https://github.com/cloudera/hue/tree/master/vue3-webcomponent-wrapper#readme",
+  "devDependencies": {}
+}

+ 22 - 1
webpack.config.npm.js

@@ -123,4 +123,25 @@ const parserConfig = Object.assign({}, defaultConfig, {
   }
 });
 
-module.exports = [libConfig, webComponentsConfig, parserConfig];
+const WRAPPER_DIR = `${__dirname}/tools/vue3-webcomponent-wrapper`;
+const vue3WebCompWrapperConfig = Object.assign({}, defaultConfig, {
+  entry: {
+    index: [`${JS_ROOT}/vue/wrapper/index.ts`]
+  },
+  output: {
+    path: `${WRAPPER_DIR}/dist`,
+    library: '[name]',
+    libraryExport: 'default',
+    libraryTarget: 'umd',
+    umdNamedDefine: true,
+    globalObject: `(typeof self !== 'undefined' ? self : this)`
+  },
+  plugins: [
+    new CleanWebpackPlugin([`${WRAPPER_DIR}/src`, `${WRAPPER_DIR}/dist`]),
+    new CopyWebpackPlugin({
+      patterns: [{ from: `${JS_ROOT}/vue/wrapper/`, to: `${WRAPPER_DIR}/src` }]
+    })
+  ]
+});
+
+module.exports = [libConfig, webComponentsConfig, parserConfig, vue3WebCompWrapperConfig];