├── .gitignore ├── .idea ├── .gitignore ├── modules.xml ├── v-runtime-template-vue3.iml └── vcs.xml ├── .vscode └── settings.json ├── App.vue ├── LICENSE ├── README.md ├── Test.vue ├── dist ├── vue3-runtime-template.es.js ├── vue3-runtime-template.es.js.map ├── vue3-runtime-template.js ├── vue3-runtime-template.js.map ├── vue3-runtime-template.umd.js └── vue3-runtime-template.umd.js.map ├── index.js ├── main.js ├── package-lock.json ├── package.json ├── test.plugin.js ├── vue.config.js └── vue3-runtime-template.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/v-runtime-template-vue3.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": false, 3 | } -------------------------------------------------------------------------------- /App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 53 | 54 | 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alex Jover 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3-runtime-template 2 | 3 | [![npm](https://img.shields.io/npm/v/vue3-runtime-template.svg)](https://www.npmjs.com/package/vue3-runtime-template) 4 | 5 | [![npm](https://img.shields.io/npm/dm/vue3-runtime-template.svg)](https://www.npmjs.com/package/vue3-runtime-template) 6 | 7 | A Vue.js components that makes easy compiling and interpreting a Vue.js template at runtime by using a `v-html` like API. 8 | 9 | vue3-runtime-template is based off v-runtime-template but tweaked to work with Vue 3. 10 | 11 | ## Motivation 12 | 13 | This library solves the case where you get a vue-syntax template string on runtime, usually from a server. Think of a feature where you allow the user to create their own interfaces and structures. You save that as a vue template in your database, which your UI will request later. While components are pre-compiled at build time, this case isn't (since the template is received at runtime) and needs to be compiled at runtime. 14 | 15 | vue3-runtime-template compiles that template and attaches it to the scope of the component that uses it, so it has access to its data, props, methods and computed properties. 16 | 17 | Think of it as the `v-html` equivalent that also understands vue template syntax (while `v-html` is just for plain HTML). 18 | 19 | ## Getting Started 20 | 21 | Install it: 22 | 23 | ``` 24 | npm install vue3-runtime-template 25 | ``` 26 | 27 | You must **use the with-compiler Vue.js version**. This is needed in order to compile on-the-fly Vue.js templates. For that, you can set a webpack alias for `vue` to the correct file. 28 | 29 | For example, if you use the [Vue CLI](https://github.com/vuejs/vue-cli), create or modify the `vue.config.js` file adding the following alias: 30 | 31 | ```js 32 | // vue.config.js 33 | module.exports = { 34 | configureWebpack: { 35 | resolve: { 36 | alias: { 37 | vue$: 'vue/dist/vue.esm-bundler.js', 38 | // ... 39 | ``` 40 | 41 | In [Nuxt v2](http://nuxtjs.org/), open the `nuxt.config.js` file and extend the webpack config by adding the following line to the `extend` key: 42 | 43 | ```js 44 | // nuxt.config.js 45 | { 46 | build: { 47 | extend(config, { isDev, isClient }) { 48 | config.resolve.alias["vue"] = "vue.esm-bundler.js"; 49 | // ... 50 | ``` 51 | 52 | In [Nuxt v3](https://v3.nuxtjs.org/), open the `nuxt.config.js` file and extend the vite config by adding the following hook, just on client: 53 | 54 | ```js 55 | // nuxt.config.js 56 | { 57 | (...) 58 | 59 | hooks: { 60 | 'vite:extendConfig': (config, { isClient, isServer }) => { 61 | if (isClient) { 62 | config.resolve.alias.vue = 'vue/dist/vue.esm-bundler' 63 | } 64 | }, 65 | }, 66 | 67 | (...) 68 | ``` 69 | 70 | You can read about different bundles of Vue in the official [help guides](https://v3.vuejs.org/guide/installation.html#with-a-bundler). 71 | ## Usage 72 | 73 | You just need to import the `vue3-runtime-template` component, and pass the template you want: 74 | 75 | ```html 76 | 81 | 82 | 99 | ``` 100 | 101 | The template you pass **have access to the parent component instance**. For example, in the last example we're using the `AppMessage` component and accessing the `{{ name }}` state variable. 102 | 103 | But you can access computed properties and methods as well from the template: 104 | 105 | ```js 106 | export default { 107 | data: () => ({ 108 | name: "Mellow", 109 | template: ` 110 |
111 | Hello {{ name }}! 112 | 113 |

{{ someComputed }}

114 |
115 | `, 116 | }), 117 | computed: { 118 | someComputed() { 119 | return "Wow, I'm computed"; 120 | }, 121 | }, 122 | methods: { 123 | sayHi() { 124 | console.log("Hi"); 125 | }, 126 | }, 127 | }; 128 | ``` 129 | 130 | ## Limitations 131 | 132 | Keep in mind that the template can only access the instance properties of the component who is using it. 133 | 134 | ## Comparison 135 | 136 | ### vue3-runtime-template VS v-html 137 | 138 | _TL;DR: If you need to interpret only HTML, use `v-html`. Use this library otherwise._ 139 | 140 | They both have the same goal: to interpret and attach a piece of structure to a scope at runtime. The difference is, `[v-html](https://vuejs.org/v2/api/#v-html)` doesn't understand vue template syntax, but only HTML. So, while this code works: 141 | 142 | ```html 143 | 146 | 147 | 11 | 12 | -------------------------------------------------------------------------------- /dist/vue3-runtime-template.es.js: -------------------------------------------------------------------------------- 1 | import{h as t}from"vue";var o=function(t,o,e){if(!o.hasOwnProperty(e)){var r=Object.getOwnPropertyDescriptor(t,e);Object.defineProperty(o,e,r)}};export default{props:{template:String,parent:Object,templateProps:{type:Object,default:function(){return{}}}},render:function(){if(this.template){var e=this.parent||this.$parent,r=e.$data;void 0===r&&(r={});var n=e.$props;void 0===n&&(n={});var a=e.$options;void 0===a&&(a={});var p=a.components;void 0===p&&(p={});var i=a.computed;void 0===i&&(i={});var c=a.methods;void 0===c&&(c={});var s=this.$data;void 0===s&&(s={});var d=this.$props;void 0===d&&(d={});var v=this.$options;void 0===v&&(v={});var m=v.methods;void 0===m&&(m={});var f=v.computed;void 0===f&&(f={});var u=v.components;void 0===u&&(u={});var h={$data:{},$props:{},$options:{},components:{},computed:{},methods:{}};Object.keys(r).forEach(function(t){void 0===s[t]&&(h.$data[t]=r[t])}),Object.keys(n).forEach(function(t){void 0===d[t]&&(h.$props[t]=n[t])}),Object.keys(c).forEach(function(t){void 0===m[t]&&(h.methods[t]=c[t])}),Object.keys(i).forEach(function(t){void 0===f[t]&&(h.computed[t]=i[t])}),Object.keys(p).forEach(function(t){void 0===u[t]&&(h.components[t]=p[t])});var $=Object.keys(h.methods||{}),O=Object.keys(h.$data||{}),b=Object.keys(h.$props||{}),j=Object.keys(this.templateProps),y=O.concat(b).concat($).concat(j),k=(E=e,P={},$.forEach(function(t){return o(E,P,t)}),P),l=function(t){var e={};return t.forEach(function(t){t&&Object.getOwnPropertyNames(t).forEach(function(r){return o(t,e,r)})}),e}([h.$data,h.$props,k,this.templateProps]);return t({template:this.template||"
",props:y,computed:h.computed,components:h.components,provide:this.$parent.$.provides?this.$parent.$.provides:{}},Object.assign({},l))}var E,P}}; 2 | //# sourceMappingURL=vue3-runtime-template.es.js.map 3 | -------------------------------------------------------------------------------- /dist/vue3-runtime-template.es.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"vue3-runtime-template.es.js","sources":["../index.js"],"sourcesContent":["import {h} from 'vue';\n\nconst defineDescriptor = (src, dest, name) => {\n // eslint-disable-next-line no-prototype-builtins\n if (!dest.hasOwnProperty(name)) {\n const descriptor = Object.getOwnPropertyDescriptor(src, name);\n Object.defineProperty(dest, name, descriptor);\n }\n};\n\nconst merge = (objs) => {\n const res = {};\n objs.forEach((obj) => {\n obj &&\n Object.getOwnPropertyNames(obj).forEach((name) =>\n defineDescriptor(obj, res, name),\n );\n });\n return res;\n};\n\nconst buildFromProps = (obj, props) => {\n const res = {};\n props.forEach((prop) => defineDescriptor(obj, res, prop));\n return res;\n};\n\nexport default {\n props: {\n template: String,\n parent: Object,\n templateProps: {\n type: Object,\n default: () => ({}),\n },\n },\n render() {\n if (this.template) {\n const parent = this.parent || this.$parent;\n const {\n $data: parentData = {},\n $props: parentProps = {},\n $options: parentOptions = {},\n } = parent;\n const {\n components: parentComponents = {},\n computed: parentComputed = {},\n methods: parentMethods = {},\n } = parentOptions;\n const {\n $data = {},\n $props = {},\n $options: {methods = {}, computed = {}, components = {}} = {},\n } = this;\n const passthrough = {\n $data: {},\n $props: {},\n $options: {},\n components: {},\n computed: {},\n methods: {},\n };\n\n // build new objects by removing keys if already exists (e.g. created by mixins)\n Object.keys(parentData).forEach((e) => {\n if (typeof $data[e] === 'undefined') {\n passthrough.$data[e] = parentData[e];\n }\n });\n Object.keys(parentProps).forEach((e) => {\n if (typeof $props[e] === 'undefined') {\n passthrough.$props[e] = parentProps[e];\n }\n });\n Object.keys(parentMethods).forEach((e) => {\n if (typeof methods[e] === 'undefined') {\n passthrough.methods[e] = parentMethods[e];\n }\n });\n Object.keys(parentComputed).forEach((e) => {\n if (typeof computed[e] === 'undefined') {\n passthrough.computed[e] = parentComputed[e];\n }\n });\n Object.keys(parentComponents).forEach((e) => {\n if (typeof components[e] === 'undefined') {\n passthrough.components[e] = parentComponents[e];\n }\n });\n\n const methodKeys = Object.keys(passthrough.methods || {});\n const dataKeys = Object.keys(passthrough.$data || {});\n const propKeys = Object.keys(passthrough.$props || {});\n const templatePropKeys = Object.keys(this.templateProps);\n const allKeys = dataKeys.concat(propKeys).concat(methodKeys).concat(templatePropKeys);\n const methodsFromProps = buildFromProps(parent, methodKeys);\n const finalProps = merge([\n passthrough.$data,\n passthrough.$props,\n methodsFromProps,\n this.templateProps,\n ]);\n\n const provide = this.$parent.$.provides ? this.$parent.$.provides : {}; // Avoids Vue warning\n\n const dynamic = {\n template: this.template || '
',\n props: allKeys,\n computed: passthrough.computed,\n components: passthrough.components,\n provide: provide,\n };\n // debugger;\n\n return h(dynamic, {...finalProps});\n }\n },\n};\n"],"names":["const","defineDescriptor","src","dest","name","hasOwnProperty","descriptor","Object","getOwnPropertyDescriptor","defineProperty","props","template","String","parent","templateProps","type","default","render","this","$parent","passthrough","$data","$props","$options","components","computed","methods","keys","parentData","forEach","e","parentProps","parentMethods","parentComputed","parentComponents","methodKeys","dataKeys","propKeys","templatePropKeys","allKeys","concat","methodsFromProps","obj","res","prop","finalProps","objs","getOwnPropertyNames","merge","h","provide","$","provides"],"mappings":"wBAEAA,IAAMC,WAAoBC,EAAKC,EAAMC,OAE9BD,EAAKE,eAAeD,GAAO,KACxBE,EAAaC,OAAOC,yBAAyBN,EAAKE,GACxDG,OAAOE,eAAeN,EAAMC,EAAME,mBAqBvB,CACbI,MAAO,CACLC,SAAUC,OACVC,OAAQN,OACRO,cAAe,CACbC,KAAMR,OACNS,+BAGJC,qBACMC,KAAKP,SAAU,KACXE,EAASK,KAAKL,QAAUK,KAAKC,iCAEb,kCACE,oCACI,sCAGK,oCACJ,mCACF,UAMvBD,0BAHM,UAGNA,2BAFO,UAEPA,6BADyD,IAAjD,+BAAW,oCAAe,sCAAiB,QAEjDE,EAAc,CAClBC,MAAO,GACPC,OAAQ,GACRC,SAAU,GACVC,WAAY,GACZC,SAAU,GACVC,QAAS,IAIXnB,OAAOoB,KAAKC,GAAYC,iBAASC,QACP,IAAbT,EAAMS,KACfV,EAAYC,MAAMS,GAAKF,EAAWE,MAGtCvB,OAAOoB,KAAKI,GAAaF,iBAASC,QACP,IAAdR,EAAOQ,KAChBV,EAAYE,OAAOQ,GAAKC,EAAYD,MAGxCvB,OAAOoB,KAAKK,GAAeH,iBAASC,QACR,IAAfJ,EAAQI,KACjBV,EAAYM,QAAQI,GAAKE,EAAcF,MAG3CvB,OAAOoB,KAAKM,GAAgBJ,iBAASC,QACR,IAAhBL,EAASK,KAClBV,EAAYK,SAASK,GAAKG,EAAeH,MAG7CvB,OAAOoB,KAAKO,GAAkBL,iBAASC,QACR,IAAlBN,EAAWM,KACpBV,EAAYI,WAAWM,GAAKI,EAAiBJ,UAI3CK,EAAa5B,OAAOoB,KAAKP,EAAYM,SAAW,IAChDU,EAAW7B,OAAOoB,KAAKP,EAAYC,OAAS,IAC5CgB,EAAW9B,OAAOoB,KAAKP,EAAYE,QAAU,IAC7CgB,EAAmB/B,OAAOoB,KAAKT,KAAKJ,eACpCyB,EAAUH,EAASI,OAAOH,GAAUG,OAAOL,GAAYK,OAAOF,GAC9DG,GA1EYC,EA0EsB7B,EAzEtC8B,EAAM,GAyEwCR,EAxE9CN,iBAASe,UAAS3C,EAAiByC,EAAKC,EAAKC,KAC5CD,GAwEGE,WAtFGC,OACPH,EAAM,UACZG,EAAKjB,iBAASa,GACZA,GACEnC,OAAOwC,oBAAoBL,GAAKb,iBAASzB,UACvCH,EAAiByC,EAAKC,EAAKvC,OAG1BuC,EA8EgBK,CAAM,CACvB5B,EAAYC,MACZD,EAAYE,OACZmB,EACAvB,KAAKJ,uBAcAmC,EATS,CACdtC,SAAUO,KAAKP,UAAY,cAC3BD,MAAO6B,EACPd,SAAUL,EAAYK,SACtBD,WAAYJ,EAAYI,WACxB0B,QAPchC,KAAKC,QAAQgC,EAAEC,SAAWlC,KAAKC,QAAQgC,EAAEC,SAAW,IAWlD7C,iBAAIsC,QA7FJH,EAChBC"} -------------------------------------------------------------------------------- /dist/vue3-runtime-template.js: -------------------------------------------------------------------------------- 1 | var t=require("vue"),o=function(t,o,e){if(!o.hasOwnProperty(e)){var r=Object.getOwnPropertyDescriptor(t,e);Object.defineProperty(o,e,r)}},e={props:{template:String,parent:Object,templateProps:{type:Object,default:function(){return{}}}},render:function(){if(this.template){var e=this.parent||this.$parent,r=e.$data;void 0===r&&(r={});var n=e.$props;void 0===n&&(n={});var a=e.$options;void 0===a&&(a={});var p=a.components;void 0===p&&(p={});var i=a.computed;void 0===i&&(i={});var c=a.methods;void 0===c&&(c={});var s=this.$data;void 0===s&&(s={});var d=this.$props;void 0===d&&(d={});var v=this.$options;void 0===v&&(v={});var u=v.methods;void 0===u&&(u={});var h=v.computed;void 0===h&&(h={});var m=v.components;void 0===m&&(m={});var f={$data:{},$props:{},$options:{},components:{},computed:{},methods:{}};Object.keys(r).forEach(function(t){void 0===s[t]&&(f.$data[t]=r[t])}),Object.keys(n).forEach(function(t){void 0===d[t]&&(f.$props[t]=n[t])}),Object.keys(c).forEach(function(t){void 0===u[t]&&(f.methods[t]=c[t])}),Object.keys(i).forEach(function(t){void 0===h[t]&&(f.computed[t]=i[t])}),Object.keys(p).forEach(function(t){void 0===m[t]&&(f.components[t]=p[t])});var $=Object.keys(f.methods||{}),O=Object.keys(f.$data||{}),b=Object.keys(f.$props||{}),j=Object.keys(this.templateProps),y=O.concat(b).concat($).concat(j),k=(E=e,P={},$.forEach(function(t){return o(E,P,t)}),P),l=function(t){var e={};return t.forEach(function(t){t&&Object.getOwnPropertyNames(t).forEach(function(r){return o(t,e,r)})}),e}([f.$data,f.$props,k,this.templateProps]);return t.h({template:this.template||"
",props:y,computed:f.computed,components:f.components,provide:this.$parent.$.provides?this.$parent.$.provides:{}},Object.assign({},l))}var E,P}};module.exports=e; 2 | //# sourceMappingURL=vue3-runtime-template.js.map 3 | -------------------------------------------------------------------------------- /dist/vue3-runtime-template.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"vue3-runtime-template.js","sources":["../index.js"],"sourcesContent":["import {h} from 'vue';\n\nconst defineDescriptor = (src, dest, name) => {\n // eslint-disable-next-line no-prototype-builtins\n if (!dest.hasOwnProperty(name)) {\n const descriptor = Object.getOwnPropertyDescriptor(src, name);\n Object.defineProperty(dest, name, descriptor);\n }\n};\n\nconst merge = (objs) => {\n const res = {};\n objs.forEach((obj) => {\n obj &&\n Object.getOwnPropertyNames(obj).forEach((name) =>\n defineDescriptor(obj, res, name),\n );\n });\n return res;\n};\n\nconst buildFromProps = (obj, props) => {\n const res = {};\n props.forEach((prop) => defineDescriptor(obj, res, prop));\n return res;\n};\n\nexport default {\n props: {\n template: String,\n parent: Object,\n templateProps: {\n type: Object,\n default: () => ({}),\n },\n },\n render() {\n if (this.template) {\n const parent = this.parent || this.$parent;\n const {\n $data: parentData = {},\n $props: parentProps = {},\n $options: parentOptions = {},\n } = parent;\n const {\n components: parentComponents = {},\n computed: parentComputed = {},\n methods: parentMethods = {},\n } = parentOptions;\n const {\n $data = {},\n $props = {},\n $options: {methods = {}, computed = {}, components = {}} = {},\n } = this;\n const passthrough = {\n $data: {},\n $props: {},\n $options: {},\n components: {},\n computed: {},\n methods: {},\n };\n\n // build new objects by removing keys if already exists (e.g. created by mixins)\n Object.keys(parentData).forEach((e) => {\n if (typeof $data[e] === 'undefined') {\n passthrough.$data[e] = parentData[e];\n }\n });\n Object.keys(parentProps).forEach((e) => {\n if (typeof $props[e] === 'undefined') {\n passthrough.$props[e] = parentProps[e];\n }\n });\n Object.keys(parentMethods).forEach((e) => {\n if (typeof methods[e] === 'undefined') {\n passthrough.methods[e] = parentMethods[e];\n }\n });\n Object.keys(parentComputed).forEach((e) => {\n if (typeof computed[e] === 'undefined') {\n passthrough.computed[e] = parentComputed[e];\n }\n });\n Object.keys(parentComponents).forEach((e) => {\n if (typeof components[e] === 'undefined') {\n passthrough.components[e] = parentComponents[e];\n }\n });\n\n const methodKeys = Object.keys(passthrough.methods || {});\n const dataKeys = Object.keys(passthrough.$data || {});\n const propKeys = Object.keys(passthrough.$props || {});\n const templatePropKeys = Object.keys(this.templateProps);\n const allKeys = dataKeys.concat(propKeys).concat(methodKeys).concat(templatePropKeys);\n const methodsFromProps = buildFromProps(parent, methodKeys);\n const finalProps = merge([\n passthrough.$data,\n passthrough.$props,\n methodsFromProps,\n this.templateProps,\n ]);\n\n const provide = this.$parent.$.provides ? this.$parent.$.provides : {}; // Avoids Vue warning\n\n const dynamic = {\n template: this.template || '
',\n props: allKeys,\n computed: passthrough.computed,\n components: passthrough.components,\n provide: provide,\n };\n // debugger;\n\n return h(dynamic, {...finalProps});\n }\n },\n};\n"],"names":["defineDescriptor","src","dest","name","hasOwnProperty","descriptor","Object","getOwnPropertyDescriptor","defineProperty","props","template","String","parent","templateProps","type","default","render","this","$parent","passthrough","$data","$props","$options","components","computed","methods","keys","parentData","forEach","e","parentProps","parentMethods","parentComputed","parentComponents","methodKeys","dataKeys","propKeys","templatePropKeys","allKeys","concat","methodsFromProps","obj","res","prop","finalProps","objs","getOwnPropertyNames","merge","h","provide","$","provides"],"mappings":"qBAEMA,WAAoBC,EAAKC,EAAMC,OAE9BD,EAAKE,eAAeD,GAAO,KACxBE,EAAaC,OAAOC,yBAAyBN,EAAKE,GACxDG,OAAOE,eAAeN,EAAMC,EAAME,OAqBvB,CACbI,MAAO,CACLC,SAAUC,OACVC,OAAQN,OACRO,cAAe,CACbC,KAAMR,OACNS,+BAGJC,qBACMC,KAAKP,SAAU,KACXE,EAASK,KAAKL,QAAUK,KAAKC,iCAEb,kCACE,oCACI,sCAGK,oCACJ,mCACF,UAMvBD,0BAHM,UAGNA,2BAFO,UAEPA,6BADyD,IAAjD,+BAAW,oCAAe,sCAAiB,QAEjDE,EAAc,CAClBC,MAAO,GACPC,OAAQ,GACRC,SAAU,GACVC,WAAY,GACZC,SAAU,GACVC,QAAS,IAIXnB,OAAOoB,KAAKC,GAAYC,iBAASC,QACP,IAAbT,EAAMS,KACfV,EAAYC,MAAMS,GAAKF,EAAWE,MAGtCvB,OAAOoB,KAAKI,GAAaF,iBAASC,QACP,IAAdR,EAAOQ,KAChBV,EAAYE,OAAOQ,GAAKC,EAAYD,MAGxCvB,OAAOoB,KAAKK,GAAeH,iBAASC,QACR,IAAfJ,EAAQI,KACjBV,EAAYM,QAAQI,GAAKE,EAAcF,MAG3CvB,OAAOoB,KAAKM,GAAgBJ,iBAASC,QACR,IAAhBL,EAASK,KAClBV,EAAYK,SAASK,GAAKG,EAAeH,MAG7CvB,OAAOoB,KAAKO,GAAkBL,iBAASC,QACR,IAAlBN,EAAWM,KACpBV,EAAYI,WAAWM,GAAKI,EAAiBJ,UAI3CK,EAAa5B,OAAOoB,KAAKP,EAAYM,SAAW,IAChDU,EAAW7B,OAAOoB,KAAKP,EAAYC,OAAS,IAC5CgB,EAAW9B,OAAOoB,KAAKP,EAAYE,QAAU,IAC7CgB,EAAmB/B,OAAOoB,KAAKT,KAAKJ,eACpCyB,EAAUH,EAASI,OAAOH,GAAUG,OAAOL,GAAYK,OAAOF,GAC9DG,GA1EYC,EA0EsB7B,EAzEtC8B,EAAM,GAyEwCR,EAxE9CN,iBAASe,UAAS3C,EAAiByC,EAAKC,EAAKC,KAC5CD,GAwEGE,WAtFGC,OACPH,EAAM,UACZG,EAAKjB,iBAASa,GACZA,GACEnC,OAAOwC,oBAAoBL,GAAKb,iBAASzB,UACvCH,EAAiByC,EAAKC,EAAKvC,OAG1BuC,EA8EgBK,CAAM,CACvB5B,EAAYC,MACZD,EAAYE,OACZmB,EACAvB,KAAKJ,uBAcAmC,IATS,CACdtC,SAAUO,KAAKP,UAAY,cAC3BD,MAAO6B,EACPd,SAAUL,EAAYK,SACtBD,WAAYJ,EAAYI,WACxB0B,QAPchC,KAAKC,QAAQgC,EAAEC,SAAWlC,KAAKC,QAAQgC,EAAEC,SAAW,IAWlD7C,iBAAIsC,QA7FJH,EAChBC"} -------------------------------------------------------------------------------- /dist/vue3-runtime-template.umd.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("vue")):"function"==typeof define&&define.amd?define(["vue"],e):t.vue3RuntimeTemplate=e(t.vue)}(this,function(t){var e=function(t,e,o){if(!e.hasOwnProperty(o)){var r=Object.getOwnPropertyDescriptor(t,o);Object.defineProperty(e,o,r)}};return{props:{template:String,parent:Object,templateProps:{type:Object,default:function(){return{}}}},render:function(){if(this.template){var o=this.parent||this.$parent,r=o.$data;void 0===r&&(r={});var n=o.$props;void 0===n&&(n={});var i=o.$options;void 0===i&&(i={});var p=i.components;void 0===p&&(p={});var a=i.computed;void 0===a&&(a={});var c=i.methods;void 0===c&&(c={});var s=this.$data;void 0===s&&(s={});var d=this.$props;void 0===d&&(d={});var v=this.$options;void 0===v&&(v={});var u=v.methods;void 0===u&&(u={});var f=v.computed;void 0===f&&(f={});var m=v.components;void 0===m&&(m={});var h={$data:{},$props:{},$options:{},components:{},computed:{},methods:{}};Object.keys(r).forEach(function(t){void 0===s[t]&&(h.$data[t]=r[t])}),Object.keys(n).forEach(function(t){void 0===d[t]&&(h.$props[t]=n[t])}),Object.keys(c).forEach(function(t){void 0===u[t]&&(h.methods[t]=c[t])}),Object.keys(a).forEach(function(t){void 0===f[t]&&(h.computed[t]=a[t])}),Object.keys(p).forEach(function(t){void 0===m[t]&&(h.components[t]=p[t])});var $=Object.keys(h.methods||{}),O=Object.keys(h.$data||{}),y=Object.keys(h.$props||{}),b=Object.keys(this.templateProps),j=O.concat(y).concat($).concat(b),l=(E=o,P={},$.forEach(function(t){return e(E,P,t)}),P),k=function(t){var o={};return t.forEach(function(t){t&&Object.getOwnPropertyNames(t).forEach(function(r){return e(t,o,r)})}),o}([h.$data,h.$props,l,this.templateProps]);return t.h({template:this.template||"
",props:j,computed:h.computed,components:h.components,provide:this.$parent.$.provides?this.$parent.$.provides:{}},Object.assign({},k))}var E,P}}}); 2 | //# sourceMappingURL=vue3-runtime-template.umd.js.map 3 | -------------------------------------------------------------------------------- /dist/vue3-runtime-template.umd.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"vue3-runtime-template.umd.js","sources":["../index.js"],"sourcesContent":["import {h} from 'vue';\n\nconst defineDescriptor = (src, dest, name) => {\n // eslint-disable-next-line no-prototype-builtins\n if (!dest.hasOwnProperty(name)) {\n const descriptor = Object.getOwnPropertyDescriptor(src, name);\n Object.defineProperty(dest, name, descriptor);\n }\n};\n\nconst merge = (objs) => {\n const res = {};\n objs.forEach((obj) => {\n obj &&\n Object.getOwnPropertyNames(obj).forEach((name) =>\n defineDescriptor(obj, res, name),\n );\n });\n return res;\n};\n\nconst buildFromProps = (obj, props) => {\n const res = {};\n props.forEach((prop) => defineDescriptor(obj, res, prop));\n return res;\n};\n\nexport default {\n props: {\n template: String,\n parent: Object,\n templateProps: {\n type: Object,\n default: () => ({}),\n },\n },\n render() {\n if (this.template) {\n const parent = this.parent || this.$parent;\n const {\n $data: parentData = {},\n $props: parentProps = {},\n $options: parentOptions = {},\n } = parent;\n const {\n components: parentComponents = {},\n computed: parentComputed = {},\n methods: parentMethods = {},\n } = parentOptions;\n const {\n $data = {},\n $props = {},\n $options: {methods = {}, computed = {}, components = {}} = {},\n } = this;\n const passthrough = {\n $data: {},\n $props: {},\n $options: {},\n components: {},\n computed: {},\n methods: {},\n };\n\n // build new objects by removing keys if already exists (e.g. created by mixins)\n Object.keys(parentData).forEach((e) => {\n if (typeof $data[e] === 'undefined') {\n passthrough.$data[e] = parentData[e];\n }\n });\n Object.keys(parentProps).forEach((e) => {\n if (typeof $props[e] === 'undefined') {\n passthrough.$props[e] = parentProps[e];\n }\n });\n Object.keys(parentMethods).forEach((e) => {\n if (typeof methods[e] === 'undefined') {\n passthrough.methods[e] = parentMethods[e];\n }\n });\n Object.keys(parentComputed).forEach((e) => {\n if (typeof computed[e] === 'undefined') {\n passthrough.computed[e] = parentComputed[e];\n }\n });\n Object.keys(parentComponents).forEach((e) => {\n if (typeof components[e] === 'undefined') {\n passthrough.components[e] = parentComponents[e];\n }\n });\n\n const methodKeys = Object.keys(passthrough.methods || {});\n const dataKeys = Object.keys(passthrough.$data || {});\n const propKeys = Object.keys(passthrough.$props || {});\n const templatePropKeys = Object.keys(this.templateProps);\n const allKeys = dataKeys.concat(propKeys).concat(methodKeys).concat(templatePropKeys);\n const methodsFromProps = buildFromProps(parent, methodKeys);\n const finalProps = merge([\n passthrough.$data,\n passthrough.$props,\n methodsFromProps,\n this.templateProps,\n ]);\n\n const provide = this.$parent.$.provides ? this.$parent.$.provides : {}; // Avoids Vue warning\n\n const dynamic = {\n template: this.template || '
',\n props: allKeys,\n computed: passthrough.computed,\n components: passthrough.components,\n provide: provide,\n };\n // debugger;\n\n return h(dynamic, {...finalProps});\n }\n },\n};\n"],"names":["const","defineDescriptor","src","dest","name","hasOwnProperty","descriptor","Object","getOwnPropertyDescriptor","defineProperty","props","template","String","parent","templateProps","type","default","render","this","$parent","passthrough","$data","$props","$options","components","computed","methods","keys","parentData","forEach","e","parentProps","parentMethods","parentComputed","parentComponents","methodKeys","dataKeys","propKeys","templatePropKeys","allKeys","concat","methodsFromProps","obj","res","prop","finalProps","objs","getOwnPropertyNames","merge","h","provide","$","provides"],"mappings":"8MAEAA,IAAMC,WAAoBC,EAAKC,EAAMC,OAE9BD,EAAKE,eAAeD,GAAO,KACxBE,EAAaC,OAAOC,yBAAyBN,EAAKE,GACxDG,OAAOE,eAAeN,EAAMC,EAAME,WAqBvB,CACbI,MAAO,CACLC,SAAUC,OACVC,OAAQN,OACRO,cAAe,CACbC,KAAMR,OACNS,+BAGJC,qBACMC,KAAKP,SAAU,KACXE,EAASK,KAAKL,QAAUK,KAAKC,iCAEb,kCACE,oCACI,sCAGK,oCACJ,mCACF,UAMvBD,0BAHM,UAGNA,2BAFO,UAEPA,6BADyD,IAAjD,+BAAW,oCAAe,sCAAiB,QAEjDE,EAAc,CAClBC,MAAO,GACPC,OAAQ,GACRC,SAAU,GACVC,WAAY,GACZC,SAAU,GACVC,QAAS,IAIXnB,OAAOoB,KAAKC,GAAYC,iBAASC,QACP,IAAbT,EAAMS,KACfV,EAAYC,MAAMS,GAAKF,EAAWE,MAGtCvB,OAAOoB,KAAKI,GAAaF,iBAASC,QACP,IAAdR,EAAOQ,KAChBV,EAAYE,OAAOQ,GAAKC,EAAYD,MAGxCvB,OAAOoB,KAAKK,GAAeH,iBAASC,QACR,IAAfJ,EAAQI,KACjBV,EAAYM,QAAQI,GAAKE,EAAcF,MAG3CvB,OAAOoB,KAAKM,GAAgBJ,iBAASC,QACR,IAAhBL,EAASK,KAClBV,EAAYK,SAASK,GAAKG,EAAeH,MAG7CvB,OAAOoB,KAAKO,GAAkBL,iBAASC,QACR,IAAlBN,EAAWM,KACpBV,EAAYI,WAAWM,GAAKI,EAAiBJ,UAI3CK,EAAa5B,OAAOoB,KAAKP,EAAYM,SAAW,IAChDU,EAAW7B,OAAOoB,KAAKP,EAAYC,OAAS,IAC5CgB,EAAW9B,OAAOoB,KAAKP,EAAYE,QAAU,IAC7CgB,EAAmB/B,OAAOoB,KAAKT,KAAKJ,eACpCyB,EAAUH,EAASI,OAAOH,GAAUG,OAAOL,GAAYK,OAAOF,GAC9DG,GA1EYC,EA0EsB7B,EAzEtC8B,EAAM,GAyEwCR,EAxE9CN,iBAASe,UAAS3C,EAAiByC,EAAKC,EAAKC,KAC5CD,GAwEGE,WAtFGC,OACPH,EAAM,UACZG,EAAKjB,iBAASa,GACZA,GACEnC,OAAOwC,oBAAoBL,GAAKb,iBAASzB,UACvCH,EAAiByC,EAAKC,EAAKvC,OAG1BuC,EA8EgBK,CAAM,CACvB5B,EAAYC,MACZD,EAAYE,OACZmB,EACAvB,KAAKJ,uBAcAmC,IATS,CACdtC,SAAUO,KAAKP,UAAY,cAC3BD,MAAO6B,EACPd,SAAUL,EAAYK,SACtBD,WAAYJ,EAAYI,WACxB0B,QAPchC,KAAKC,QAAQgC,EAAEC,SAAWlC,KAAKC,QAAQgC,EAAEC,SAAW,IAWlD7C,iBAAIsC,QA7FJH,EAChBC"} -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import {h} from 'vue'; 2 | 3 | const defineDescriptor = (src, dest, name) => { 4 | // eslint-disable-next-line no-prototype-builtins 5 | if (!dest.hasOwnProperty(name)) { 6 | const descriptor = Object.getOwnPropertyDescriptor(src, name); 7 | Object.defineProperty(dest, name, descriptor); 8 | } 9 | }; 10 | 11 | const merge = (objs) => { 12 | const res = {}; 13 | objs.forEach((obj) => { 14 | obj && 15 | Object.getOwnPropertyNames(obj).forEach((name) => 16 | defineDescriptor(obj, res, name), 17 | ); 18 | }); 19 | return res; 20 | }; 21 | 22 | const buildFromProps = (obj, props) => { 23 | const res = {}; 24 | props.forEach((prop) => defineDescriptor(obj, res, prop)); 25 | return res; 26 | }; 27 | 28 | export default { 29 | props: { 30 | template: String, 31 | parent: Object, 32 | templateProps: { 33 | type: Object, 34 | default: () => ({}), 35 | }, 36 | }, 37 | render() { 38 | if (this.template) { 39 | const parent = this.parent || this.$parent; 40 | const { 41 | $data: parentData = {}, 42 | $props: parentProps = {}, 43 | $options: parentOptions = {}, 44 | } = parent; 45 | const { 46 | components: parentComponents = {}, 47 | computed: parentComputed = {}, 48 | methods: parentMethods = {}, 49 | } = parentOptions; 50 | const { 51 | $data = {}, 52 | $props = {}, 53 | $options: {methods = {}, computed = {}, components = {}} = {}, 54 | } = this; 55 | const passthrough = { 56 | $data: {}, 57 | $props: {}, 58 | $options: {}, 59 | components: {}, 60 | computed: {}, 61 | methods: {}, 62 | }; 63 | 64 | // build new objects by removing keys if already exists (e.g. created by mixins) 65 | Object.keys(parentData).forEach((e) => { 66 | if (typeof $data[e] === 'undefined') { 67 | passthrough.$data[e] = parentData[e]; 68 | } 69 | }); 70 | Object.keys(parentProps).forEach((e) => { 71 | if (typeof $props[e] === 'undefined') { 72 | passthrough.$props[e] = parentProps[e]; 73 | } 74 | }); 75 | Object.keys(parentMethods).forEach((e) => { 76 | if (typeof methods[e] === 'undefined') { 77 | passthrough.methods[e] = parentMethods[e]; 78 | } 79 | }); 80 | Object.keys(parentComputed).forEach((e) => { 81 | if (typeof computed[e] === 'undefined') { 82 | passthrough.computed[e] = parentComputed[e]; 83 | } 84 | }); 85 | Object.keys(parentComponents).forEach((e) => { 86 | if (typeof components[e] === 'undefined') { 87 | passthrough.components[e] = parentComponents[e]; 88 | } 89 | }); 90 | 91 | const methodKeys = Object.keys(passthrough.methods || {}); 92 | const dataKeys = Object.keys(passthrough.$data || {}); 93 | const propKeys = Object.keys(passthrough.$props || {}); 94 | const templatePropKeys = Object.keys(this.templateProps); 95 | const allKeys = dataKeys.concat(propKeys).concat(methodKeys).concat(templatePropKeys); 96 | const methodsFromProps = buildFromProps(parent, methodKeys); 97 | const finalProps = merge([ 98 | passthrough.$data, 99 | passthrough.$props, 100 | methodsFromProps, 101 | this.templateProps, 102 | ]); 103 | 104 | const provide = this.$parent.$.provides ? this.$parent.$.provides : {}; // Avoids Vue warning 105 | 106 | const dynamic = { 107 | template: this.template || '
', 108 | props: allKeys, 109 | computed: passthrough.computed, 110 | components: passthrough.components, 111 | provide: provide, 112 | }; 113 | // debugger; 114 | 115 | return h(dynamic, {...finalProps}); 116 | } 117 | }, 118 | }; 119 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | 4 | import testPlugin from "./test.plugin.js"; //testing mixins 5 | 6 | Vue.use(testPlugin); 7 | 8 | new Vue({ 9 | render: h => h(App) 10 | }).$mount("#app"); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-runtime-template", 3 | "version": "1.0.2", 4 | "description": "Create Vue 3 components by compiling templates on the fly", 5 | "main": "dist/vue3-runtime-template.umd.js", 6 | "scripts": { 7 | "prepublishOnly": "npm run build", 8 | "build": "microbundle index.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/mattelen/vue3-runtime-template.git" 14 | }, 15 | "keywords": [ 16 | "vuejs", 17 | "vue3", 18 | "dynamic", 19 | "runtime", 20 | "template" 21 | ], 22 | "author": { 23 | "name": "Alex J", 24 | "email": "alexjovermorales@gmail.com" 25 | }, 26 | "contributors": [ 27 | { 28 | "name": "Matt Elen", 29 | "url": "https://github.com/mattelen" 30 | } 31 | ], 32 | "license": "MIT", 33 | "devDependencies": { 34 | "acorn": "^8.0.4", 35 | "microbundle": "^0.11.0" 36 | }, 37 | "bugs": { 38 | "url": "https://github.com/mattelen/vue3-runtime-template/issues" 39 | }, 40 | "homepage": "https://github.com/mattelen/vue3-runtime-template#readme", 41 | "module": "dist/vue3-runtime-template.es.js", 42 | "unpkg": "dist/vue3-runtime-template.umd.js", 43 | "browser": "dist/vue3-runtime-template.es.js", 44 | "types": "vue3-runtime-template.d.ts", 45 | "dependencies": { 46 | "vue": "^3.0.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test.plugin.js: -------------------------------------------------------------------------------- 1 | //https://vuejs.org/v2/guide/plugins.html 2 | //https://dev.to/nkoik/writing-a-very-simple-plugin-in-vuejs---example-8g8 3 | // This exports the plugin object. 4 | 5 | import Test from "./Test.vue" 6 | 7 | export default { 8 | // The install method will be called with the Vue constructor as 9 | // the first argument, along with possible options 10 | install(Vue) { 11 | Vue.mixin({ 12 | components:{Test}, 13 | props: { 14 | testingProp: { 15 | default: "mixinTest: testingProp" 16 | } 17 | }, 18 | data() { 19 | return { 20 | testingData: "mixinTest: testingData" 21 | }; 22 | }, 23 | computed: { 24 | testingComputed() { 25 | return "mixinTest: testingComputed"; 26 | } 27 | }, 28 | methods: { 29 | testingMethod() { 30 | return "mixinTest: testingMethod"; 31 | } 32 | } 33 | }); //end mixin 34 | 35 | Vue.prototype.$testProto = function (str) { 36 | return "mixinTest: testingProto=" + str; 37 | }; //end $testProto 38 | 39 | } 40 | }; -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | // vue.config.js 2 | 3 | module.exports = { 4 | runtimeCompiler: true, 5 | configureWebpack: { 6 | resolve: { 7 | alias: { 8 | vue$: "vue/dist/vue.common" 9 | } 10 | } 11 | } 12 | }; -------------------------------------------------------------------------------- /vue3-runtime-template.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'vue3-runtime-template' 2 | --------------------------------------------------------------------------------