11<template >
2- <div >
3- <div style =" color :red " v-if =" error" >{{this.error}}</div >
4- <component v-if =" !error && previewedComponent" :id =" scope" :is =" previewedComponent" />
5- </div >
2+ <pre :class =" $style.error" v-if =" error" >{{ this.error }}</pre >
3+ <component
4+ v-else-if =" previewedComponent"
5+ :id =" scope"
6+ :is =" previewedComponent"
7+ :key =" iteration"
8+ />
69</template >
710
811<script >
912import {
10- compile ,
13+ compile as compileScript ,
1114 isCodeVueSfc ,
1215 addScopedStyle ,
1316 adaptCreateElement ,
14- concatenate
17+ concatenate ,
1518} from " vue-inbrowser-compiler" ;
19+ import checkTemplate, {
20+ VueLiveUndefinedVariableError ,
21+ } from " ./utils/checkTemplate" ;
1622import evalInContext from " ./utils/evalInContext" ;
1723import requireAtRuntime from " ./utils/requireAtRuntime" ;
1824
1925export default {
2026 name: " VueLivePreviewComponent" ,
2127 components: {},
28+ errorCaptured (err ) {
29+ this .handleError (err);
30+ },
2231 props: {
2332 /**
2433 * code rendered
2534 */
2635 code: {
2736 type: String ,
28- required: true
37+ required: true ,
2938 },
3039 /**
3140 * Hashtable of auto-registered components
@@ -34,7 +43,7 @@ export default {
3443 */
3544 components: {
3645 type: Object ,
37- default : () => {}
46+ default : () => {},
3847 },
3948 /**
4049 * Hashtable of modules available in require and import statements
@@ -44,26 +53,27 @@ export default {
4453 */
4554 requires: {
4655 type: Object ,
47- default : () => {}
56+ default : () => {},
4857 },
4958 jsx: {
5059 type: Boolean ,
51- default: false
60+ default: false ,
5261 },
5362 /**
5463 * Outside data to the preview
5564 * @example { count: 1 }
5665 */
5766 dataScope: {
5867 type: Object ,
59- default : () => {}
60- }
68+ default : () => {},
69+ },
6170 },
6271 data () {
6372 return {
6473 scope: this .generateScope (),
6574 previewedComponent: undefined ,
66- error: false
75+ iteration: 0 ,
76+ error: false ,
6777 };
6878 },
6979 created () {
@@ -72,28 +82,32 @@ export default {
7282 watch: {
7383 code (value ) {
7484 this .renderComponent (value .trim ());
75- }
85+ },
7686 },
7787 methods: {
7888 /**
7989 * Generates the Scope Id attribute value. It will be added to each
8090 * tag if a style is applied to scope the style only to this example
8191 */
8292 generateScope () {
83- return " v-xxxxxxxx" .replace (/ [xy] / g , c => {
93+ return " v-xxxxxxxx" .replace (/ [xy] / g , ( c ) => {
8494 const r = (Math .random () * 16 ) | 0 ;
8595 const v = c === " x" ? r : (r & 0x3 ) | 0x8 ;
8696 return v .toString (16 );
8797 });
8898 },
8999 handleError (e ) {
100+ this .$emit (" error" , e);
101+ if (e .constructor === VueLiveUndefinedVariableError) {
102+ e .message = ` Cannot parse template expression: ${ e .expression } \n\n ${ e .message } ` ;
103+ }
90104 this .error = e .message ;
91105 },
92106 renderComponent (code ) {
93- let data = {};
107+ let options = {};
94108 let style;
95109 try {
96- const renderedComponent = compile (
110+ const renderedComponent = compileScript (
97111 code,
98112 this .jsx
99113 ? { jsx: " __pragma__(h)" , objectAssign: " __concatenate__" }
@@ -110,46 +124,63 @@ export default {
110124 // - a script setting up variables => we set up the data property of renderedComponent
111125 // - a `new Vue()` script that will return a full config object
112126 const script = renderedComponent .script ;
113- data =
127+ options =
114128 evalInContext (
115129 script,
116- filepath => requireAtRuntime (this .requires , filepath),
130+ ( filepath ) => requireAtRuntime (this .requires , filepath),
117131 adaptCreateElement,
118132 concatenate
119133 ) || {};
120134
121135 if (this .dataScope ) {
122- const mergeData = { ... data .data (), ... this .dataScope };
123- data .data = () => mergeData;
136+ const mergeData = { ... options .data (), ... this .dataScope };
137+ options .data = () => mergeData;
124138 }
125139 }
126140 if (renderedComponent .template ) {
127141 // if this is a pure template or if we are in hybrid vsg mode,
128142 // we need to set the template up.
129- data .template = ` <div>${ renderedComponent .template } </div>` ;
143+ options .template = ` <div>${ renderedComponent .template } </div>` ;
130144 }
131145 } catch (e) {
132146 this .handleError (e);
133147 return ;
134148 }
135149
136- data .components = this .components ;
150+ try {
151+ checkTemplate (options .template , options);
152+ } catch (e) {
153+ this .handleError (e);
154+ return ;
155+ }
156+
157+ options .components = this .components ;
137158 if (style) {
138159 // To add the scope id attribute to each item in the html
139160 // this way when we add the scoped style sheet it will be aplied
140- data ._scopeId = ` data-${ this .scope } ` ;
161+ options ._scopeId = ` data-${ this .scope } ` ;
141162 addScopedStyle (style, this .scope );
142163 }
143164
144- if (data .template || data .render ) {
145- this .previewedComponent = data;
165+ if (options .template || options .render ) {
166+ this .previewedComponent = options;
167+ this .iteration = this .iteration + 1 ;
146168 } else {
147169 this .handleError ({
148170 message:
149- " [Vue Live] no template or render function specified, you might have an issue in your example"
171+ " [Vue Live] no template or render function specified, you might have an issue in your example" ,
150172 });
151173 }
152- }
153- }
174+ },
175+ },
154176};
155177 </script >
178+
179+ <style module>
180+ .error {
181+ color : red ;
182+ text-align : left ;
183+ overflow : auto ;
184+ white-space : pre-wrap ;
185+ }
186+ </style >
0 commit comments