diff --git a/src/compile/nodes/Component.ts b/src/compile/nodes/Component.ts index 10ba66c413..9465e2aaa7 100644 --- a/src/compile/nodes/Component.ts +++ b/src/compile/nodes/Component.ts @@ -5,7 +5,7 @@ import stringifyProps from '../../utils/stringifyProps'; import CodeBuilder from '../../utils/CodeBuilder'; import getTailSnippet from '../../utils/getTailSnippet'; import getObject from '../../utils/getObject'; -import { quoteNameIfNecessary } from '../../utils/quoteIfNecessary'; +import { quoteNameIfNecessary, quotePropIfNecessary } from '../../utils/quoteIfNecessary'; import { escape, escapeTemplate, stringify } from '../../utils/stringify'; import Node from './shared/Node'; import Block from '../dom/Block'; @@ -219,7 +219,7 @@ export default class Component extends Node { updates.push(deindent` if (${[...attribute.dependencies] .map(dependency => `changed.${dependency}`) - .join(' || ')}) ${name_changes}.${attribute.name} = ${attribute.getValue()}; + .join(' || ')}) ${name_changes}${quotePropIfNecessary(attribute.name)} = ${attribute.getValue()}; `); } }); @@ -250,10 +250,10 @@ export default class Component extends Node { const lhs = binding.value.node.type === 'MemberExpression' ? binding.value.snippet - : `${head}${tail} = childState.${binding.name}`; + : `${head}${tail} = childState${quotePropIfNecessary(binding.name)}`; setFromChild = deindent` - ${lhs} = childState.${binding.name}; + ${lhs} = childState${quotePropIfNecessary(binding.name)}; ${[...binding.value.dependencies] .map((name: string) => { @@ -264,7 +264,7 @@ export default class Component extends Node { if (isStoreProp) hasStoreBindings = true; else hasLocalBindings = true; - return `${newState}.${prop} = ctx.${name};`; + return `${newState}${quotePropIfNecessary(prop)} = ctx${quotePropIfNecessary(name)};`; })} `; } @@ -279,32 +279,32 @@ export default class Component extends Node { if (binding.value.node.type === 'MemberExpression') { setFromChild = deindent` - ${binding.value.snippet} = childState.${binding.name}; - ${newState}.${prop} = ctx.${key}; + ${binding.value.snippet} = childState${quotePropIfNecessary(binding.name)}; + ${newState}${quotePropIfNecessary(prop)} = ctx${quotePropIfNecessary(key)}; `; } else { - setFromChild = `${newState}.${prop} = childState.${binding.name};`; + setFromChild = `${newState}${quotePropIfNecessary(prop)} = childState${quotePropIfNecessary(binding.name)};`; } } statements.push(deindent` if (${binding.value.snippet} !== void 0) { - ${name_initial_data}.${binding.name} = ${binding.value.snippet}; - ${name_updating}.${binding.name} = true; + ${name_initial_data}${quotePropIfNecessary(binding.name)} = ${binding.value.snippet}; + ${name_updating}${quotePropIfNecessary(binding.name)} = true; }` ); builder.addConditional( - `!${name_updating}.${binding.name} && changed.${binding.name}`, + `!${name_updating}${quotePropIfNecessary(binding.name)} && changed${quotePropIfNecessary(binding.name)}`, setFromChild ); updates.push(deindent` - if (!${name_updating}.${binding.name} && ${[...binding.value.dependencies].map((dependency: string) => `changed.${dependency}`).join(' || ')}) { - ${name_changes}.${binding.name} = ${binding.value.snippet}; - ${name_updating}.${binding.name} = ${binding.value.snippet} !== void 0; + if (!${name_updating}${quotePropIfNecessary(binding.name)} && ${[...binding.value.dependencies].map((dependency: string) => `changed.${dependency}`).join(' || ')}) { + ${name_changes}${quotePropIfNecessary(binding.name)} = ${binding.value.snippet}; + ${name_updating}${quotePropIfNecessary(binding.name)} = ${binding.value.snippet} !== void 0; } `); }); @@ -329,7 +329,7 @@ export default class Component extends Node { beforecreate = deindent` #component.root._beforecreate.push(() => { - ${name}._bind({ ${this.bindings.map(b => `${b.name}: 1`).join(', ')} }, ${name}.get()); + ${name}._bind({ ${this.bindings.map(b => `${quoteNameIfNecessary(b.name)}: 1`).join(', ')} }, ${name}.get()); }); `; } @@ -519,7 +519,7 @@ export default class Component extends Node { ? getTailSnippet(binding.value.node) : ''; - return `${binding.name}: ctx.${name}${tail}`; + return `${quoteNameIfNecessary(binding.name)}: ctx${quotePropIfNecessary(name)}${tail}`; }); function getAttributeValue(attribute) { @@ -547,14 +547,14 @@ export default class Component extends Node { if (attribute.isSpread) { return attribute.expression.snippet; } else { - return `{ ${attribute.name}: ${getAttributeValue(attribute)} }`; + return `{ ${quoteNameIfNecessary(attribute.name)}: ${getAttributeValue(attribute)} }`; } }) .concat(bindingProps.map(p => `{ ${p} }`)) .join(', ') })` : `{ ${this.attributes - .map(attribute => `${attribute.name}: ${getAttributeValue(attribute)}`) + .map(attribute => `${quoteNameIfNecessary(attribute.name)}: ${getAttributeValue(attribute)}`) .concat(bindingProps) .join(', ')} }`; @@ -585,7 +585,7 @@ export default class Component extends Node { if (${conditions.reverse().join('&&')}) { tmp = ${expression}.data(); if ('${name}' in tmp) { - ctx.${binding.name} = tmp.${name}; + ctx${quotePropIfNecessary(binding.name)} = tmp.${name}; settled = false; } } diff --git a/test/runtime/samples/component-binding-invalid-identifier/Counter.html b/test/runtime/samples/component-binding-invalid-identifier/Counter.html new file mode 100644 index 0000000000..afb4ac7d58 --- /dev/null +++ b/test/runtime/samples/component-binding-invalid-identifier/Counter.html @@ -0,0 +1,14 @@ + + + diff --git a/test/runtime/samples/component-binding-invalid-identifier/_config.js b/test/runtime/samples/component-binding-invalid-identifier/_config.js new file mode 100644 index 0000000000..aa64ab81b8 --- /dev/null +++ b/test/runtime/samples/component-binding-invalid-identifier/_config.js @@ -0,0 +1,29 @@ +export default { + 'skip-ssr': true, // TODO delete this line, once binding works + + html: ` + +
count: 0
+ `, + + test ( assert, component, target, window ) { + const click = new window.MouseEvent( 'click' ); + const button = target.querySelector( 'button' ); + + button.dispatchEvent( click ); + + assert.equal( component.get().x, 1 ); + assert.htmlEqual( target.innerHTML, ` + +count: 1
+ ` ); + + button.dispatchEvent( click ); + + assert.equal( component.get().x, 2 ); + assert.htmlEqual( target.innerHTML, ` + +count: 2
+ ` ); + } +}; diff --git a/test/runtime/samples/component-binding-invalid-identifier/main.html b/test/runtime/samples/component-binding-invalid-identifier/main.html new file mode 100644 index 0000000000..90dcbfe34d --- /dev/null +++ b/test/runtime/samples/component-binding-invalid-identifier/main.html @@ -0,0 +1,12 @@ +count: {x}
+ + diff --git a/test/runtime/samples/component-invalid-identifier/Widget.html b/test/runtime/samples/component-invalid-identifier/Widget.html new file mode 100644 index 0000000000..779a912f2d --- /dev/null +++ b/test/runtime/samples/component-invalid-identifier/Widget.html @@ -0,0 +1,10 @@ +{state["b-c"]}
+ diff --git a/test/runtime/samples/component-invalid-identifier/_config.js b/test/runtime/samples/component-invalid-identifier/_config.js new file mode 100644 index 0000000000..d768ccd1c7 --- /dev/null +++ b/test/runtime/samples/component-invalid-identifier/_config.js @@ -0,0 +1,3 @@ +export default { + html: 'i am a widget