Merge branch 'master' into gh-424

pull/434/head
Rich-Harris 8 years ago
commit 1a49c2f344

@ -3,7 +3,7 @@ import flattenReference from '../../../../utils/flattenReference.js';
import getSetter from './binding/getSetter.js';
export default function createBinding ( generator, node, attribute, current, local ) {
const { name } = flattenReference( attribute.value );
const { name, keypath } = flattenReference( attribute.value );
const { snippet, contexts, dependencies } = generator.contextualise( attribute.value );
if ( dependencies.length > 1 ) throw new Error( 'An unexpected situation arose. Please raise an issue at https://github.com/sveltejs/svelte/issues — thanks!' );
@ -35,7 +35,7 @@ export default function createBinding ( generator, node, attribute, current, loc
prop
});
const setter = getSetter({ current, name, context: '_context', attribute, dependencies, snippet, value: 'value' });
const setter = getSetter({ current, name, keypath, context: '_context', attribute, dependencies, value: 'value' });
generator.hasComplexBindings = true;

@ -184,7 +184,7 @@ export default function addElementAttributes ( generator, node, local ) {
local.init.addBlock( deindent`
var ${handlerName} = ${generator.alias( 'template' )}.events.${name}.call( ${generator.current.component}, ${local.name}, function ( event ) {
${handlerBody}
});
}.bind( ${local.name} ) );
` );
generator.current.builders.teardown.addLine( deindent`

@ -21,7 +21,7 @@ export default function createBinding ( generator, node, attribute, current, loc
const value = getBindingValue( generator, local, node, attribute, isMultipleSelect, bindingGroup, type );
const eventName = getBindingEventName( node );
let setter = getSetter({ current, name, context: '__svelte', attribute, dependencies, snippet, value });
let setter = getSetter({ current, name, keypath, context: '__svelte', attribute, dependencies, value });
let updateElement;
// <select> special case

@ -1,6 +1,6 @@
import deindent from '../../../../../utils/deindent.js';
export default function getSetter ({ current, name, context, attribute, dependencies, snippet, value }) {
export default function getSetter ({ current, name, keypath, context, attribute, dependencies, value }) {
if ( current.contexts.has( name ) ) {
const prop = dependencies[0];
const tail = attribute.value.type === 'MemberExpression' ? getTailSnippet( attribute.value ) : '';
@ -17,7 +17,7 @@ export default function getSetter ({ current, name, context, attribute, dependen
if ( attribute.value.type === 'MemberExpression' ) {
return deindent`
var ${name} = ${current.component}.get( '${name}' );
${snippet} = ${value};
${keypath} = ${value};
${current.component}._set({ ${name}: ${name} });
`;
}

@ -109,7 +109,7 @@ const lookup = {
title: {},
type: { appliesTo: [ 'button', 'input', 'command', 'embed', 'object', 'script', 'source', 'style', 'menu' ] },
usemap: { propertyName: 'useMap', appliesTo: [ 'img', 'input', 'object' ] },
value: { appliesTo: [ 'button', 'option', 'input', 'li', 'meter', 'progress', 'param' ] },
value: { appliesTo: [ 'button', 'option', 'input', 'li', 'meter', 'progress', 'param', 'select' ] },
width: { appliesTo: [ 'canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video' ] },
wrap: { appliesTo: [ 'textarea' ] }
};

@ -1,5 +1,7 @@
export default function flatten ( node ) {
const parts = [];
const propEnd = node.end;
while ( node.type === 'MemberExpression' ) {
if ( node.computed ) return null;
parts.unshift( node.property.name );
@ -7,10 +9,11 @@ export default function flatten ( node ) {
node = node.object;
}
const propStart = node.end;
const name = node.type === 'Identifier' ? node.name : node.type === 'ThisExpression' ? 'this' : null;
if ( !name ) return null;
parts.unshift( name );
return { name, parts, keypath: parts.join( '.' ) };
return { name, parts, keypath: `${name}[✂${propStart}-${propEnd}✂]` };
}

@ -63,5 +63,13 @@ export default function validateJs ( validator, js ) {
validator.defaultExport = node;
}
if ( node.type === 'ImportDeclaration' ) {
node.specifiers.forEach( specifier => {
if ( specifier.local.name === 'root' ) {
validator.error( `Imported identifiers cannot have a name of 'root' due to technical limitations`, specifier.start );
}
});
}
});
}

@ -0,0 +1,5 @@
<select bind:value='selectedComponent'>
{{#each components as component}}
<option value='{{component}}'>{{component.name}}.html</option>
{{/each}}
</select>

@ -0,0 +1 @@
<textarea bind:value='code'></textarea>

@ -0,0 +1,84 @@
const components = [
{
name: 'One',
source: 'one source'
},
{
name: 'Two',
source: 'two source'
}
];
const selectedComponent = components[0];
export default {
skip: true, // doesn't reflect real-world bug, maybe a JSDOM quirk
data: {
components,
selectedComponent
},
html: `
<select>
<option value='[object Object]'>One.html</option>
<option value='[object Object]'>Two.html</option>
</select>
<textarea></textarea>
<pre>ONE SOURCE\nTWO SOURCE</pre>
`,
test ( assert, component, target, window ) {
const event = new window.MouseEvent( 'input' );
const textarea = target.querySelector( 'textarea' );
textarea.value = 'one source changed';
textarea.dispatchEvent( event );
assert.equal( component.get( 'compiled' ), 'ONE SOURCE CHANGED\nTWO SOURCE' );
assert.htmlEqual( target.innerHTML, `
<select>
<option value='[object Object]'>One.html</option>
<option value='[object Object]'>Two.html</option>
</select>
<textarea></textarea>
<pre>ONE SOURCE CHANGED\nTWO SOURCE</pre>
` );
// const select = target.querySelector( 'select' );
// console.log( `select.options[0].selected`, select.options[0].selected )
// console.log( `select.options[1].selected`, select.options[1].selected )
// console.log( `select.value`, select.value )
// console.log( `select.__value`, select.__value )
// select.options[1].selected = true;
// console.log( `select.options[0].selected`, select.options[0].selected )
// console.log( `select.options[1].selected`, select.options[1].selected )
// console.log( `select.value`, select.value )
// console.log( `select.__value`, select.__value )
// select.dispatchEvent( new window.Event( 'change' ) );
component.set({ selectedComponent: components[1] });
assert.equal( textarea.value, 'two source' );
textarea.value = 'two source changed';
textarea.dispatchEvent( event );
assert.equal( component.get( 'compiled' ), 'ONE SOURCE CHANGED\nTWO SOURCE CHANGED' );
assert.htmlEqual( target.innerHTML, `
<select>
<option value='[object Object]'>One.html</option>
<option value='[object Object]'>Two.html</option>
</select>
<textarea></textarea>
<pre>ONE SOURCE CHANGED\nTWO SOURCE CHANGED</pre>
` );
component.destroy();
}
};

@ -0,0 +1,42 @@
<ComponentSelector :components bind:selectedComponent/>
<Editor bind:code='selectedComponent.source'/>
<pre>
{{compiled}}
</pre>
<script>
import Editor from './Editor.html';
import ComponentSelector from './ComponentSelector.html';
export default {
components: {
ComponentSelector,
Editor
},
oncreate () {
this.observe( 'components', components => {
components.forEach( component => {
if ( component === this.get( 'selectedComponent' ) ) return;
component.compiled = component.source.toUpperCase();
});
});
this.observe( 'selectedComponent', component => {
component.compiled = component.source.toUpperCase();
this.updateBundle();
});
},
methods: {
updateBundle () {
const components = this.get( 'components' );
const compiled = components.map( component => component.compiled ).join( '\n' );
this.set({ compiled });
}
}
}
</script>

@ -0,0 +1,15 @@
export default {
'skip-ssr': true,
html: '<button>10</button>',
test ( assert, component, target, window ) {
const event = new window.MouseEvent( 'click' );
const button = target.querySelector( 'button' );
button.dispatchEvent( event );
assert.equal( target.innerHTML, '<button>11</button>' );
}
};

@ -0,0 +1,25 @@
<button on:tap='set({ z: z + 1 })'>{{z}}</button>
<script>
export default {
data: () => ({
z: 10
}),
events: {
tap ( node, callback ) {
const clickHandler = event => {
callback(event);
};
node.addEventListener( 'click', clickHandler, false );
return {
teardown () {
node.addEventListener( 'click', clickHandler, false );
}
};
}
}
};
</script>

@ -0,0 +1,19 @@
export default {
'skip-ssr': true,
data: {
foo: 'a'
},
test ( assert, component, target ) {
const options = target.querySelectorAll( 'option' );
assert.equal( options[0].selected, true );
assert.equal( options[1].selected, false );
component.set( { foo: 'b' } );
assert.equal( options[0].selected, false );
assert.equal( options[1].selected, true );
}
};

@ -0,0 +1,4 @@
<select value="{{foo}}">
<option>a</option>
<option>b</option>
</select>

@ -1,10 +1,10 @@
export function test ({ assert, smc, locateInSource, locateInGenerated }) {
const expected = locateInSource( 'foo.bar.baz' );
const expected = locateInSource( 'bar.baz' );
let loc;
let actual;
loc = locateInGenerated( 'foo.bar.baz' );
loc = locateInGenerated( 'bar.baz' );
actual = smc.originalPositionFor({
line: loc.line + 1,
@ -18,7 +18,7 @@ export function test ({ assert, smc, locateInSource, locateInGenerated }) {
column: expected.column
});
loc = locateInGenerated( 'foo.bar.baz', loc.character + 1 );
loc = locateInGenerated( 'bar.baz', loc.character + 1 );
actual = smc.originalPositionFor({
line: loc.line + 1,

@ -0,0 +1,8 @@
[{
"message": "Imported identifiers cannot have a name of 'root' due to technical limitations",
"pos": 17,
"loc": {
"line": 2,
"column": 8
}
}]

@ -0,0 +1,3 @@
<script>
import root from 'foo';
</script>
Loading…
Cancel
Save