Skip to content

Commit 369801b

Browse files
committed
fix: avoid checking "value" and allow false
closes #36
1 parent 0596e25 commit 369801b

File tree

6 files changed

+74
-26
lines changed

6 files changed

+74
-26
lines changed

demo/App.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
/>
5050
<h2>Custom delay</h2>
5151
<p>
52-
when updates should not hapen too often, for instance when a component
53-
need a lot of computing power to render, One can change the standard
52+
When updates should not happen too often, for instance when a component
53+
need a lot of computing power to render, one can change the standard
5454
throttle timing.
5555
</p>
5656
<VueLive

package-lock.json

Lines changed: 13 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@
1717
"core-js": "^3.6.5",
1818
"debounce": "^1.2.0",
1919
"hash-sum": "^2.0.0",
20+
"lodash.has": "^4.5.2",
2021
"prismjs": "^1.16.0",
2122
"recast": "^0.19.1",
22-
"vue-gh-corners": "^3.0.1",
23-
"vue-github-corners": "^1.2.3",
2423
"vue-inbrowser-compiler": "^3.21.0",
2524
"vue-prism-editor": "^0.3.0",
2625
"vue-template-compiler": "^2.0.0"
@@ -46,6 +45,8 @@
4645
"rollup-plugin-vue": "^5.1.6",
4746
"validate-commit-msg": "^2.14.0",
4847
"vue": "^2.6.10",
48+
"vue-gh-corners": "^3.0.1",
49+
"vue-github-corners": "^1.2.3",
4950
"vue-template-compiler": "^2.6.10",
5051
"vuejs-datepicker": "^1.6.2"
5152
},

src/Preview.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
concatenate,
1818
} from "vue-inbrowser-compiler";
1919
import checkTemplate, {
20-
VueLiveUndefinedVariableError,
20+
VueLiveParseTemplateError,
2121
} from "./utils/checkTemplate";
2222
import evalInContext from "./utils/evalInContext";
2323
import requireAtRuntime from "./utils/requireAtRuntime";
@@ -98,7 +98,7 @@ export default {
9898
},
9999
handleError(e) {
100100
this.$emit("error", e);
101-
if (e.constructor === VueLiveUndefinedVariableError) {
101+
if (e.constructor === VueLiveParseTemplateError) {
102102
e.message = `Cannot parse template expression: ${e.expression}\n\n${e.message}`;
103103
}
104104
this.error = e.message;
@@ -148,7 +148,7 @@ export default {
148148
}
149149
150150
try {
151-
checkTemplate(options.template, options);
151+
checkTemplate(options);
152152
} catch (e) {
153153
this.handleError(e);
154154
return;

src/utils/__tests__/checkTemplate.js

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,86 @@ import checkTemplate from "../checkTemplate";
22

33
test("parse valid template without error with a function", () => {
44
expect(() =>
5-
checkTemplate('<div><a :href="foo()">hello</a></div>')
5+
checkTemplate({ template: '<div><a :href="foo()">hello</a></div>' })
66
).not.toThrow();
77
});
88

99
test("parse valid template without error with a value", () => {
1010
expect(() =>
11-
checkTemplate('<div><compo :value="today">hello</compo></div>', {
11+
checkTemplate({
12+
template: '<div><compo :value="today">hello</compo></div>',
1213
data() {
1314
return {
14-
today: "hello"
15+
today: "hello",
1516
};
16-
}
17+
},
18+
})
19+
).not.toThrow();
20+
});
21+
22+
test("parse false value as a valid value", () => {
23+
expect(() =>
24+
checkTemplate({
25+
template: `
26+
<div>
27+
<input v-model="value" type="checkbox">
28+
<h1 v-if="value">I am checked</h1>
29+
</div>`,
30+
data() {
31+
return {
32+
value: false,
33+
};
34+
},
1735
})
1836
).not.toThrow();
1937
});
2038

2139
test("parse invalid template with an error in the ++", () => {
2240
expect(() =>
23-
checkTemplate('<div><compo :value="today++">hello</compo></div>')
41+
checkTemplate({
42+
template: '<div><compo :value="today++">hello</compo></div>',
43+
})
2444
).toThrow();
2545
});
2646

2747
test("parse invalid template with an error in a function call", () => {
2848
expect(() =>
29-
checkTemplate('<div><compo :value="callit(today)">hello</compo></div>')
49+
checkTemplate({
50+
template: '<div><compo :value="callit(today)">hello</compo></div>',
51+
})
3052
).toThrow();
3153
});
3254

3355
test("parse invalid template with an error in a function call and a spread", () => {
3456
expect(() =>
35-
checkTemplate('<div><compo :value="callit(...today)">hello</compo></div>')
57+
checkTemplate({
58+
template: '<div><compo :value="callit(...today)">hello</compo></div>',
59+
})
3660
).toThrow();
3761
});
3862

3963
test("parse invalid template with an error if the value is not in data", () => {
4064
expect(() =>
41-
checkTemplate('<div><compo :value="today">hello</compo></div>')
65+
checkTemplate({
66+
template: '<div><compo :value="today">hello</compo></div>',
67+
})
4268
).toThrowErrorMatchingInlineSnapshot(
4369
`"Variable \\"today\\" is not defined."`
4470
);
4571
});
4672

4773
test("parse invalid : template by throwing an error", () => {
4874
expect(() =>
49-
checkTemplate('<div><a :href="+++foo()">hello</a></div>')
75+
checkTemplate({
76+
template: '<div><a :href="+++foo()">hello</a></div>',
77+
})
5078
).toThrowErrorMatchingInlineSnapshot(`"Assigning to rvalue (1:9)"`);
5179
});
5280

5381
test("parse invalid @ template by throwing an error", () => {
5482
expect(() =>
55-
checkTemplate('<div><a @click="+++foo()">hello</a></div>')
83+
checkTemplate({
84+
template: '<div><a @click="+++foo()">hello</a></div>',
85+
})
5686
).toThrowErrorMatchingInlineSnapshot(`"Assigning to rvalue (1:9)"`);
5787
});

src/utils/checkTemplate.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
import { compile } from "vue-template-compiler";
22
import { parse } from "acorn";
3-
import * as recast from "recast";
3+
import { visit } from "recast";
4+
import has from "lodash.has";
45

5-
export default function(src, $options) {
6-
const { ast } = compile(src);
6+
export default function($options) {
7+
if (!$options.template) {
8+
return;
9+
}
10+
const { ast } = compile($options.template);
711
traverse(ast, (templateAst) => {
812
if (templateAst.type !== 1) {
913
return;
1014
}
1115
templateAst.attrsList
1216
// for all attribute that has an expression
13-
.filter((attr) => /^[:,@,v-]/.test(attr.name))
17+
.filter((attr) => /^(:|@|v-)/.test(attr.name))
1418
.forEach((attr) => {
1519
try {
1620
// try and parse the expression
1721
const ast = parse(`() => {${attr.value}}`);
1822
// identify all variables that would be undefined because not in the data object
19-
recast.visit(ast, {
23+
visit(ast, {
2024
visitIdentifier(identifier) {
2125
const varName = identifier.value.name;
2226
if (
@@ -25,7 +29,10 @@ export default function(src, $options) {
2529
identifier.parentPath.name === "arguments") &&
2630
(!$options ||
2731
typeof $options.data !== "function" ||
28-
!$options.data()[varName])
32+
(!has($options.data(), varName) &&
33+
!has($options.props, varName) &&
34+
Array.isArray($options.props) &&
35+
$options.props.indexOf(varName) === -1))
2936
) {
3037
throw new VueLiveUndefinedVariableError(
3138
`Variable "${varName}" is not defined.`,

0 commit comments

Comments
 (0)