Merge branch 'master' into universal-tests

pull/253/head
Rich Harris 9 years ago
commit c66474e8bb

@ -1,5 +1,25 @@
# Svelte changelog # Svelte changelog
## 1.6.7
* SSR: Fix apostrophes ([#267](https://github.com/sveltejs/svelte/issues/267))
* Add `xmlns` attributes to SVGs ([#262](https://github.com/sveltejs/svelte/pull/262))
## 1.6.6
* Omit text from comment anchors ([#247](https://github.com/sveltejs/svelte/issues/247))
* Handle `xlink` attributes ([#264](https://github.com/sveltejs/svelte/issues/264))
## 1.6.5
* Handle `<!doctype>` declarations ([#255](https://github.com/sveltejs/svelte/pull/255))
## 1.6.4
* Fix updates of yields inside each blocks ([20e1b05](https://github.com/sveltejs/svelte/commit/20e1b05c45dc9fcddfe2e7c5c9fc3109f0d45fa9))
* SSR: Handle attributes with values that begin with a number ([#248](https://github.com/sveltejs/svelte/issues/248))
* Handle multiline comments in CSS ([#252](https://github.com/sveltejs/svelte/issues/252))
## 1.6.3 ## 1.6.3
* Fix `{{yield}}` bugs for components inside `if` and `each` blocks ([#230](https://github.com/sveltejs/svelte/issues/230), [#231](https://github.com/sveltejs/svelte/issues/231)) * Fix `{{yield}}` bugs for components inside `if` and `each` blocks ([#230](https://github.com/sveltejs/svelte/issues/230), [#231](https://github.com/sveltejs/svelte/issues/231))

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "1.6.3", "version": "1.6.7",
"description": "The magical disappearing UI framework", "description": "The magical disappearing UI framework",
"main": "compiler/svelte.js", "main": "compiler/svelte.js",
"files": [ "files": [

@ -98,9 +98,9 @@ class DomGenerator extends Generator {
` ); ` );
} }
createAnchor ( name, description = '' ) { createAnchor ( name ) {
this.uses.createComment = true; this.uses.createComment = true;
const renderStatement = `createComment( ${JSON.stringify( description )} )`; const renderStatement = `createComment()`;
this.addElement( name, renderStatement, true ); this.addElement( name, renderStatement, true );
} }
@ -174,11 +174,11 @@ export default function dom ( parsed, source, options, names ) {
const builders = { const builders = {
main: new CodeBuilder(), main: new CodeBuilder(),
init: new CodeBuilder(), init: new CodeBuilder(),
set: new CodeBuilder() _set: new CodeBuilder()
}; };
builders.set.addLine( 'var oldState = this._state;' ); builders._set.addLine( 'var oldState = this._state;' );
builders.set.addLine( 'this._state = Object.assign( {}, oldState, newState );' ); builders._set.addLine( 'this._state = Object.assign( {}, oldState, newState );' );
if ( computations.length ) { if ( computations.length ) {
const builder = new CodeBuilder(); const builder = new CodeBuilder();
@ -197,11 +197,11 @@ export default function dom ( parsed, source, options, names ) {
} }
` ); ` );
builders.set.addLine( `applyComputations( this._state, newState, oldState )` ); builders._set.addLine( `applyComputations( this._state, newState, oldState )` );
} }
// TODO is the `if` necessary? // TODO is the `if` necessary?
builders.set.addBlock( deindent` builders._set.addBlock( deindent`
dispatchObservers( this, this._observers.pre, newState, oldState ); dispatchObservers( this, this._observers.pre, newState, oldState );
if ( this._fragment ) this._fragment.update( newState, this._state ); if ( this._fragment ) this._fragment.update( newState, this._state );
dispatchObservers( this, this._observers.post, newState, oldState ); dispatchObservers( this, this._observers.post, newState, oldState );
@ -246,7 +246,7 @@ export default function dom ( parsed, source, options, names ) {
while ( this._bindings.length ) this._bindings.pop()(); while ( this._bindings.length ) this._bindings.pop()();
` ); ` );
builders.set.addLine( `while ( this._bindings.length ) this._bindings.pop()();` ); builders._set.addLine( `while ( this._bindings.length ) this._bindings.pop()();` );
} else { } else {
builders.init.addBlock( deindent` builders.init.addBlock( deindent`
this._fragment = renderMainFragment( this._state, this ); this._fragment = renderMainFragment( this._state, this );
@ -255,15 +255,10 @@ export default function dom ( parsed, source, options, names ) {
} }
if ( generator.hasComponents ) { if ( generator.hasComponents ) {
const statement = deindent` const statement = `this._flush();`;
while ( this._renderHooks.length ) {
var hook = this._renderHooks.pop();
hook.fn.call( hook.context );
}
`;
builders.init.addBlock( statement ); builders.init.addBlock( statement );
builders.set.addBlock( statement ); builders._set.addBlock( statement );
} }
if ( templateProperties.onrender ) { if ( templateProperties.onrender ) {
@ -310,6 +305,8 @@ export default function dom ( parsed, source, options, names ) {
${name}.prototype.fire = fire; ${name}.prototype.fire = fire;
${name}.prototype.observe = observe; ${name}.prototype.observe = observe;
${name}.prototype.on = on; ${name}.prototype.on = on;
${name}.prototype.set = set;
${name}.prototype._flush = _flush;
` : ` :
deindent` deindent`
${name}.prototype.get = ${shared.get}; ${name}.prototype.get = ${shared.get};
@ -319,11 +316,15 @@ export default function dom ( parsed, source, options, names ) {
${name}.prototype.observe = ${shared.observe}; ${name}.prototype.observe = ${shared.observe};
${name}.prototype.on = ${shared.on}; ${name}.prototype.on = ${shared.on};
${name}.prototype.set = ${shared.set};
${name}.prototype._flush = ${shared._flush};
` ); ` );
builders.main.addBlock( deindent` builders.main.addBlock( deindent`
${name}.prototype.set = function set ( newState ) { ${name}.prototype._set = function _set ( newState ) {
${builders.set} ${builders._set}
}; };
${name}.prototype.teardown = function teardown ( detach ) { ${name}.prototype.teardown = function teardown ( detach ) {
@ -341,7 +342,7 @@ export default function dom ( parsed, source, options, names ) {
throw new Error( `Components with shared helpers must be compiled to ES2015 modules (format: 'es')` ); throw new Error( `Components with shared helpers must be compiled to ES2015 modules (format: 'es')` );
} }
const names = [ 'get', 'fire', 'observe', 'on', 'dispatchObservers' ].concat( Object.keys( generator.uses ) ); const names = [ 'get', 'fire', 'observe', 'on', 'set', '_flush', 'dispatchObservers' ].concat( Object.keys( generator.uses ) );
builders.main.addLineAtStart( builders.main.addLineAtStart(
`import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )}` `import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )}`
); );

@ -21,7 +21,7 @@ export default {
const { dependencies, snippet } = generator.contextualise( node.expression ); const { dependencies, snippet } = generator.contextualise( node.expression );
const anchor = `${name}_anchor`; const anchor = `${name}_anchor`;
generator.createAnchor( anchor, `#each ${generator.source.slice( node.expression.start, node.expression.end )}` ); generator.createAnchor( anchor );
generator.current.builders.init.addLine( `var ${name}_value = ${snippet};` ); generator.current.builders.init.addLine( `var ${name}_value = ${snippet};` );
generator.current.builders.init.addLine( `var ${iterations} = [];` ); generator.current.builders.init.addLine( `var ${iterations} = [];` );

@ -41,7 +41,7 @@ export default {
const conditionsAndBlocks = getConditionsAndBlocks( generator, node, generator.getUniqueName( `renderIfBlock` ) ); const conditionsAndBlocks = getConditionsAndBlocks( generator, node, generator.getUniqueName( `renderIfBlock` ) );
const anchor = `${name}_anchor`; const anchor = `${name}_anchor`;
generator.createAnchor( anchor, `#if ${generator.source.slice( node.expression.start, node.expression.end )}` ); generator.createAnchor( anchor );
generator.current.builders.init.addBlock( deindent` generator.current.builders.init.addBlock( deindent`
function ${getBlock} ( ${params} ) { function ${getBlock} ( ${params} ) {

@ -1,7 +1,7 @@
export default { export default {
enter ( generator ) { enter ( generator ) {
const anchor = `yield_anchor`; const anchor = `yield_anchor`;
generator.createAnchor( anchor, 'yield' ); generator.createAnchor( anchor );
generator.current.builders.mount.addLine( generator.current.builders.mount.addLine(
`component._yield && component._yield.mount( ${generator.current.target}, ${anchor} );` `component._yield && component._yield.mount( ${generator.current.target}, ${anchor} );`

@ -5,15 +5,24 @@ import flattenReference from '../../../../utils/flattenReference.js';
export default function addElementAttributes ( generator, node, local ) { export default function addElementAttributes ( generator, node, local ) {
node.attributes.forEach( attribute => { node.attributes.forEach( attribute => {
const name = attribute.name;
if ( attribute.type === 'Attribute' ) { if ( attribute.type === 'Attribute' ) {
let metadata = local.namespace ? null : attributeLookup[ attribute.name ]; let metadata = local.namespace ? null : attributeLookup[ name ];
if ( metadata && metadata.appliesTo && !~metadata.appliesTo.indexOf( node.name ) ) metadata = null; if ( metadata && metadata.appliesTo && !~metadata.appliesTo.indexOf( node.name ) ) metadata = null;
let dynamic = false; let dynamic = false;
const isBoundOptionValue = node.name === 'option' && attribute.name === 'value'; // TODO check it's actually bound const isBoundOptionValue = node.name === 'option' && name === 'value'; // TODO check it's actually bound
const propertyName = isBoundOptionValue ? '__value' : metadata && metadata.propertyName; const propertyName = isBoundOptionValue ? '__value' : metadata && metadata.propertyName;
const isXlink = name.slice( 0, 6 ) === 'xlink:';
// xlink is a special case... we could maybe extend this to generic
// namespaced attributes but I'm not sure that's applicable in
// HTML5?
const helper = isXlink ? 'setXlinkAttribute' : 'setAttribute';
if ( attribute.value === true ) { if ( attribute.value === true ) {
// attributes without values, e.g. <textarea readonly> // attributes without values, e.g. <textarea readonly>
if ( propertyName ) { if ( propertyName ) {
@ -21,14 +30,14 @@ export default function addElementAttributes ( generator, node, local ) {
`${local.name}.${propertyName} = true;` `${local.name}.${propertyName} = true;`
); );
} else { } else {
generator.uses.setAttribute = true; generator.uses[ helper ] = true;
local.init.addLine( local.init.addLine(
`setAttribute( ${local.name}, '${attribute.name}', true );` `${helper}( ${local.name}, '${name}', true );`
); );
} }
// special case autofocus. has to be handled in a bit of a weird way // special case autofocus. has to be handled in a bit of a weird way
if ( attribute.name === 'autofocus' ) { if ( name === 'autofocus' ) {
generator.current.autofocus = local.name; generator.current.autofocus = local.name;
} }
} }
@ -39,9 +48,9 @@ export default function addElementAttributes ( generator, node, local ) {
`${local.name}.${propertyName} = '';` `${local.name}.${propertyName} = '';`
); );
} else { } else {
generator.uses.setAttribute = true; generator.uses[ helper ] = true;
local.init.addLine( local.init.addLine(
`setAttribute( ${local.name}, '${attribute.name}', '' );` `${helper}( ${local.name}, '${name}', '' );`
); );
} }
} }
@ -55,18 +64,24 @@ export default function addElementAttributes ( generator, node, local ) {
// static attributes // static attributes
result = JSON.stringify( value.data ); result = JSON.stringify( value.data );
if ( attribute.name === 'xmlns' ) { let addAttribute = false;
if ( name === 'xmlns' ) {
// special case // special case
// TODO this attribute must be static enforce at compile time // TODO this attribute must be static enforce at compile time
local.namespace = value.data; local.namespace = value.data;
addAttribute = true;
} else if ( propertyName ) { } else if ( propertyName ) {
local.init.addLine( local.init.addLine(
`${local.name}.${propertyName} = ${result};` `${local.name}.${propertyName} = ${result};`
); );
} else { } else {
generator.uses.setAttribute = true; addAttribute = true;
}
if ( addAttribute ) {
generator.uses[ helper ] = true;
local.init.addLine( local.init.addLine(
`setAttribute( ${local.name}, '${attribute.name}', ${result} );` `${helper}( ${local.name}, '${name}', ${result} );`
); );
} }
} }
@ -81,8 +96,8 @@ export default function addElementAttributes ( generator, node, local ) {
if (propertyName) { if (propertyName) {
updater = `${local.name}.${propertyName} = ${snippet};`; updater = `${local.name}.${propertyName} = ${snippet};`;
} else { } else {
generator.uses.setAttribute = true; generator.uses[ helper ] = true;
updater = `setAttribute( ${local.name}, '${attribute.name}', ${snippet} );`; // TODO use snippet both times see note below updater = `${helper}( ${local.name}, '${name}', ${snippet} );`; // TODO use snippet both times see note below
} }
local.init.addLine( updater ); local.init.addLine( updater );
@ -110,8 +125,8 @@ export default function addElementAttributes ( generator, node, local ) {
if (propertyName) { if (propertyName) {
updater = `${local.name}.${propertyName} = ${value};`; updater = `${local.name}.${propertyName} = ${value};`;
} else { } else {
generator.uses.setAttribute = true; generator.uses[ helper ] = true;
updater = `setAttribute( ${local.name}, '${attribute.name}', ${value} );`; updater = `${helper}( ${local.name}, '${name}', ${value} );`;
} }
local.init.addLine( updater ); local.init.addLine( updater );
@ -153,12 +168,12 @@ export default function addElementAttributes ( generator, node, local ) {
return `var ${listName} = this.__svelte.${listName}, ${indexName} = this.__svelte.${indexName}, ${name} = ${listName}[${indexName}]`; return `var ${listName} = this.__svelte.${listName}, ${indexName} = this.__svelte.${indexName}, ${name} = ${listName}[${indexName}]`;
}); });
const handlerName = generator.current.getUniqueName( `${attribute.name}Handler` ); const handlerName = generator.current.getUniqueName( `${name}Handler` );
const handlerBody = ( declarations.length ? declarations.join( '\n' ) + '\n\n' : '' ) + `[✂${attribute.expression.start}-${attribute.expression.end}✂];`; const handlerBody = ( declarations.length ? declarations.join( '\n' ) + '\n\n' : '' ) + `[✂${attribute.expression.start}-${attribute.expression.end}✂];`;
if ( attribute.name in generator.events ) { if ( name in generator.events ) {
local.init.addBlock( deindent` local.init.addBlock( deindent`
var ${handlerName} = template.events.${attribute.name}.call( component, ${local.name}, function ( event ) { var ${handlerName} = template.events.${name}.call( component, ${local.name}, function ( event ) {
${handlerBody} ${handlerBody}
}); });
` ); ` );
@ -174,11 +189,11 @@ export default function addElementAttributes ( generator, node, local ) {
${handlerBody} ${handlerBody}
} }
addEventListener( ${local.name}, '${attribute.name}', ${handlerName} ); addEventListener( ${local.name}, '${name}', ${handlerName} );
` ); ` );
generator.current.builders.teardown.addLine( deindent` generator.current.builders.teardown.addLine( deindent`
removeEventListener( ${local.name}, '${attribute.name}', ${handlerName} ); removeEventListener( ${local.name}, '${name}', ${handlerName} );
` ); ` );
} }
} }
@ -191,11 +206,11 @@ export default function addElementAttributes ( generator, node, local ) {
generator.usesRefs = true; generator.usesRefs = true;
local.init.addLine( local.init.addLine(
`component.refs.${attribute.name} = ${local.name};` `component.refs.${name} = ${local.name};`
); );
generator.current.builders.teardown.addLine( deindent` generator.current.builders.teardown.addLine( deindent`
if ( component.refs.${attribute.name} === ${local.name} ) component.refs.${attribute.name} = null; if ( component.refs.${name} === ${local.name} ) component.refs.${name} = null;
` ); ` );
} }

@ -64,7 +64,7 @@ export default function createBinding ( generator, node, attribute, current, loc
var index = this.__svelte.${indexName}; var index = this.__svelte.${indexName};
list[index]${parts.slice( 1 ).map( part => `.${part}` ).join( '' )} = ${value}; list[index]${parts.slice( 1 ).map( part => `.${part}` ).join( '' )} = ${value};
component.set({ ${prop}: component.get( '${prop}' ) }); component._set({ ${prop}: component.get( '${prop}' ) });
`; `;
} else if ( deep ) { } else if ( deep ) {
setter = deindent` setter = deindent`
@ -98,7 +98,7 @@ export default function createBinding ( generator, node, attribute, current, loc
local.update.addBlock( deindent` local.update.addBlock( deindent`
if ( !${local.name}_updating && '${parts[0]}' in changed ) { if ( !${local.name}_updating && '${parts[0]}' in changed ) {
${local.name}.set({ ${attribute.name}: ${contextual ? attribute.value : `root.${attribute.value}`} }); ${local.name}._set({ ${attribute.name}: ${contextual ? attribute.value : `root.${attribute.value}`} });
} }
` ); ` );
} else { } else {

@ -112,7 +112,7 @@ export default function ssr ( parsed, source, options, names ) {
var escaped = { var escaped = {
'"': '&quot;', '"': '&quot;',
"'": '&39;', "'": '&#39;',
'&': '&amp;', '&': '&amp;',
'<': '&lt;', '<': '&lt;',
'>': '&gt;' '>': '&gt;'

@ -1,6 +1,6 @@
// largely borrowed from Ractive https://github.com/ractivejs/ractive/blob/2ec648aaf5296bb88c21812e947e0e42fcc456e3/src/Ractive/config/custom/css/transform.js // largely borrowed from Ractive https://github.com/ractivejs/ractive/blob/2ec648aaf5296bb88c21812e947e0e42fcc456e3/src/Ractive/config/custom/css/transform.js
const selectorsPattern = /(?:^|\})?\s*([^\{\}]+)\s*\{/g; const selectorsPattern = /(?:^|\})?\s*([^\{\}]+)\s*\{/g;
const commentsPattern = /\/\*.*?\*\//g; const commentsPattern = /\/\*[\s\S]*?\*\//g;
const selectorUnitPattern = /((?:(?:\[[^\]+]\])|(?:[^\s\+\>~:]))+)((?:::?[^\s\+\>\~\(:]+(?:\([^\)]+\))?)*\s*[\s\+\>\~]?)\s*/g; const selectorUnitPattern = /((?:(?:\[[^\]+]\])|(?:[^\s\+\>~:]))+)((?:::?[^\s\+\>\~\(:]+(?:\([^\)]+\))?)*\s*[\s\+\>\~]?)\s*/g;
const excludePattern = /^(?:@|\d+%)/; const excludePattern = /^(?:@|\d+%)/;

@ -6,7 +6,7 @@ import { trimStart, trimEnd } from '../utils/trim.js';
import { decodeCharacterReferences } from '../utils/html.js'; import { decodeCharacterReferences } from '../utils/html.js';
import voidElementNames from '../../utils/voidElementNames.js'; import voidElementNames from '../../utils/voidElementNames.js';
const validTagName = /^[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const invalidUnquotedAttributeCharacters = /[\s"'=<>\/`]/; const invalidUnquotedAttributeCharacters = /[\s"'=<>\/`]/;
const specials = { const specials = {

@ -34,8 +34,8 @@ export function createText ( data ) {
return document.createTextNode( data ); return document.createTextNode( data );
} }
export function createComment ( data ) { export function createComment () {
return document.createComment( data ); return document.createComment( '' );
} }
export function addEventListener ( node, event, handler ) { export function addEventListener ( node, event, handler ) {
@ -49,3 +49,7 @@ export function removeEventListener ( node, event, handler ) {
export function setAttribute ( node, attribute, value ) { export function setAttribute ( node, attribute, value ) {
node.setAttribute ( attribute, value ); node.setAttribute ( attribute, value );
} }
export function setXlinkAttribute ( node, attribute, value ) {
node.setAttributeNS( 'http://www.w3.org/1999/xlink', attribute, value );
}

@ -41,3 +41,17 @@ export function on ( eventName, handler ) {
} }
}; };
} }
export function set ( newState ) {
this._set( newState );
( this._root || this )._flush();
}
export function _flush () {
if ( !this._renderHooks ) return;
while ( this._renderHooks.length ) {
var hook = this._renderHooks.pop();
hook.fn.call( hook.context );
}
}

@ -1 +1 @@
export default /^(?:area|base|br|col|command|doctype|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/; export default /^(?:area|base|br|col|command|\!doctype|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/;

@ -7,5 +7,5 @@ export default {
] ]
}, },
html: `<div class="foo ">1</div><div class=" bar">2</div><div class="foo bar">3</div><!--#each items-->` html: `<div class="foo ">1</div><div class=" bar">2</div><div class="foo bar">3</div><!---->`
}; };

@ -6,7 +6,7 @@ export default {
'three' 'three'
] ]
}, },
html: `<div><input><p>one</p></div><div><input><p>two</p></div><div><input><p>three</p></div><!--#each items-->`, html: `<div><input><p>one</p></div><div><input><p>two</p></div><div><input><p>three</p></div><!---->`,
test ( assert, component, target, window ) { test ( assert, component, target, window ) {
const inputs = [ ...target.querySelectorAll( 'input' ) ]; const inputs = [ ...target.querySelectorAll( 'input' ) ];
const items = component.get( 'items' ); const items = component.get( 'items' );
@ -18,12 +18,12 @@ export default {
inputs[1].dispatchEvent( event ); inputs[1].dispatchEvent( event );
assert.equal( items[1], 'four' ); assert.equal( items[1], 'four' );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>three</p></div><!--#each items-->` ); assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>three</p></div><!---->` );
items[2] = 'five'; items[2] = 'five';
component.set({ items }); component.set({ items });
assert.equal( inputs[2].value, 'five' ); assert.equal( inputs[2].value, 'five' );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>five</p></div><!--#each items-->` ); assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>five</p></div><!---->` );
} }
}; };

@ -6,7 +6,7 @@ export default {
{ description: 'three' } { description: 'three' }
] ]
}, },
html: `<div><input><p>one</p></div><div><input><p>two</p></div><div><input><p>three</p></div><!--#each items-->`, html: `<div><input><p>one</p></div><div><input><p>two</p></div><div><input><p>three</p></div><!---->`,
test ( assert, component, target, window ) { test ( assert, component, target, window ) {
const inputs = [ ...target.querySelectorAll( 'input' ) ]; const inputs = [ ...target.querySelectorAll( 'input' ) ];
@ -17,13 +17,13 @@ export default {
inputs[1].value = 'four'; inputs[1].value = 'four';
inputs[1].dispatchEvent( event ); inputs[1].dispatchEvent( event );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>three</p></div><!--#each items-->` ); assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>three</p></div><!---->` );
const items = component.get( 'items' ); const items = component.get( 'items' );
items[2].description = 'five'; items[2].description = 'five';
component.set({ items }); component.set({ items });
assert.equal( inputs[2].value, 'five' ); assert.equal( inputs[2].value, 'five' );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>five</p></div><!--#each items-->` ); assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>five</p></div><!---->` );
} }
}; };

@ -2,8 +2,8 @@ export default {
html: '<div><p>Hello</p></div>', html: '<div><p>Hello</p></div>',
test ( assert, component, target ) { test ( assert, component, target ) {
assert.equal( component.get( 'data' ), 'Hello' ); assert.equal( component.get( 'data' ), 'Hello' );
component.set({data: 'World'}) component.set({data: 'World'});
assert.equal( component.get( 'data' ), 'World' ); assert.equal( component.get( 'data' ), 'World' );
assert.equal( target.innerHTML, '<div><p>World<!--yield--></p></div>' ); assert.equal( target.innerHTML, '<div><p>World<!----></p></div>' );
}
} }
};

@ -0,0 +1,7 @@
// JSDOM makes this test pass when it should fail. weird
export default {
test ( assert, component, target, window ) {
const p = target.querySelector( 'p' );
assert.equal( window.getComputedStyle( p ).color, 'red' );
}
};

@ -0,0 +1,11 @@
<p>red</p>
<style>
/*
div {}
*/
p {
color: red;
}
</style>

@ -2,5 +2,5 @@ export default {
data: { data: {
animals: [ 'adder', 'blue whale', 'chameleon' ] animals: [ 'adder', 'blue whale', 'chameleon' ]
}, },
html: `<p>0: adder</p><p>1: blue whale</p><p>2: chameleon</p><!--#each animals-->` html: `<p>0: adder</p><p>1: blue whale</p><p>2: chameleon</p><!---->`
}; };

@ -2,11 +2,11 @@ export default {
data: { data: {
animals: [ 'alpaca', 'baboon', 'capybara' ] animals: [ 'alpaca', 'baboon', 'capybara' ]
}, },
html: '<p>alpaca</p><p>baboon</p><p>capybara</p><!--#each animals-->', html: '<p>alpaca</p><p>baboon</p><p>capybara</p><!---->',
test ( assert, component, target ) { test ( assert, component, target ) {
component.set({ animals: [ 'alpaca', 'baboon', 'caribou', 'dogfish' ] }); component.set({ animals: [ 'alpaca', 'baboon', 'caribou', 'dogfish' ] });
assert.equal( target.innerHTML, '<p>alpaca</p><p>baboon</p><p>caribou</p><p>dogfish</p><!--#each animals-->' ); assert.equal( target.innerHTML, '<p>alpaca</p><p>baboon</p><p>caribou</p><p>dogfish</p><!---->' );
component.set({ animals: [] }); component.set({ animals: [] });
assert.equal( target.innerHTML, '<!--#each animals-->' ); assert.equal( target.innerHTML, '<!---->' );
} }
}; };

@ -1,3 +1,3 @@
export default { export default {
html: `<p>a</p><p>b</p><p>c</p><!--#each [ 'a', 'b', 'c' ]-->` html: `<p>a</p><p>b</p><p>c</p><!---->`
}; };

@ -28,7 +28,7 @@ export default {
} }
] ]
}, },
html: `<p>animals: aardvark</p><p>animals: buffalo</p><p>animals: chinchilla</p><!--#each category.things--><p>countries: albania</p><p>countries: brazil</p><p>countries: china</p><!--#each category.things--><p>people: alice</p><p>people: bob</p><p>people: carol</p><p>people: dave</p><!--#each category.things--><!--#each categories-->`, html: `<p>animals: aardvark</p><p>animals: buffalo</p><p>animals: chinchilla</p><!----><p>countries: albania</p><p>countries: brazil</p><p>countries: china</p><!----><p>people: alice</p><p>people: bob</p><p>people: carol</p><p>people: dave</p><!----><!---->`,
test ( assert, component, target ) { test ( assert, component, target ) {
// TODO // TODO
} }

@ -4,7 +4,7 @@ export default {
rows: [ 1, 2, 3 ] rows: [ 1, 2, 3 ]
}, },
html: `<div>a, 1</div><div>a, 2</div><div>a, 3</div><!--#each rows--><div>b, 1</div><div>b, 2</div><div>b, 3</div><!--#each rows--><div>c, 1</div><div>c, 2</div><div>c, 3</div><!--#each rows--><!--#each columns-->`, html: `<div>a, 1</div><div>a, 2</div><div>a, 3</div><!----><div>b, 1</div><div>b, 2</div><div>b, 3</div><!----><div>c, 1</div><div>c, 2</div><div>c, 3</div><!----><!---->`,
test ( assert, component, target ) { test ( assert, component, target ) {
// TODO // TODO

@ -1,7 +1,7 @@
// TODO gah, JSDOM appears to behave differently to real browsers here... probably need to raise an issue // TODO gah, JSDOM appears to behave differently to real browsers here... probably need to raise an issue
export default { export default {
html: '<input><!--#if visible-->', html: '<input><!---->',
test ( assert, component ) { test ( assert, component ) {
component.refs.input.focus(); component.refs.input.focus();

@ -1,13 +1,13 @@
export default { export default {
html: '<button>toggle</button>\n\n<!--#if visible-->', html: '<button>toggle</button>\n\n<!---->',
test ( assert, component, target, window ) { test ( assert, component, target, window ) {
const button = target.querySelector( 'button' ); const button = target.querySelector( 'button' );
const event = new window.MouseEvent( 'click' ); const event = new window.MouseEvent( 'click' );
button.dispatchEvent( event ); button.dispatchEvent( event );
assert.equal( target.innerHTML, '<button>toggle</button>\n\n<p>hello!</p><!--#if visible-->' ); assert.equal( target.innerHTML, '<button>toggle</button>\n\n<p>hello!</p><!---->' );
button.dispatchEvent( event ); button.dispatchEvent( event );
assert.equal( target.innerHTML, '<button>toggle</button>\n\n<!--#if visible-->' ); assert.equal( target.innerHTML, '<button>toggle</button>\n\n<!---->' );
} }
}; };

@ -2,11 +2,11 @@ export default {
data: { data: {
visible: true visible: true
}, },
html: 'before\n<p>Widget</p><!--#if visible-->\nafter', html: 'before\n<p>Widget</p><!---->\nafter',
test ( assert, component, target ) { test ( assert, component, target ) {
component.set({ visible: false }); component.set({ visible: false });
assert.equal( target.innerHTML, 'before\n<!--#if visible-->\nafter' ); assert.equal( target.innerHTML, 'before\n<!---->\nafter' );
component.set({ visible: true }); component.set({ visible: true });
assert.equal( target.innerHTML, 'before\n<p>Widget</p><!--#if visible-->\nafter' ); assert.equal( target.innerHTML, 'before\n<p>Widget</p><!---->\nafter' );
} }
}; };

@ -2,11 +2,11 @@ export default {
data: { data: {
visible: true visible: true
}, },
html: '<p>i am visible</p><!--#if visible-->', html: '<p>i am visible</p><!---->',
test ( assert, component, target ) { test ( assert, component, target ) {
component.set({ visible: false }); component.set({ visible: false });
assert.equal( target.innerHTML, '<!--#if visible-->' ); assert.equal( target.innerHTML, '<!---->' );
component.set({ visible: true }); component.set({ visible: true });
assert.equal( target.innerHTML, '<p>i am visible</p><!--#if visible-->' ); assert.equal( target.innerHTML, '<p>i am visible</p><!---->' );
} }
}; };

@ -0,0 +1,16 @@
<span>{{foo}}</span>
<script>
export default {
data () {
return {
foo: 'XX'
};
},
onrender () {
this.observe( 'item', item => {
this.set({ foo: item });
});
}
};
</script>

@ -0,0 +1,25 @@
{{#each items as item}}
<Item item={{item}} />
{{/each}}
<script>
import Item from './Item.html';
export default {
data () {
return {
items: [ 3, 2, 1 ]
};
},
methods: {
update () {
this.set({
items: [ 1, 2, 3, 4, 5 ]
});
}
},
components: {
Item
}
};
</script>

@ -0,0 +1,15 @@
export default {
html: `
<span>3</span><span>2</span><span>1</span>
`,
test ( assert, component, target ) {
component.refs.list.update();
assert.htmlEqual( target.innerHTML, `
<span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
` );
component.teardown();
}
};

@ -0,0 +1,11 @@
<List ref:list/>
<script>
import List from './List.html';
export default {
components: {
List
}
};
</script>

@ -0,0 +1,19 @@
export default {
html: `
<svg>
<defs>
<circle id='stamp' r='10' fill='blue'/>
</defs>
<use xlink:href='#stamp' x='20' y='20'/>
</svg>
`,
test ( assert, component, target ) {
const use = target.querySelector( 'use' );
const href = use.attributes[ 'xlink:href' ];
assert.equal( href.namespaceURI, 'http://www.w3.org/1999/xlink' );
component.teardown();
}
};

@ -0,0 +1,7 @@
<svg>
<defs>
<circle id='stamp' r='10' fill='blue'/>
</defs>
<use xlink:href='#stamp' x='20' y='20'/>
</svg>

After

Width:  |  Height:  |  Size: 115 B

@ -8,7 +8,7 @@ export default {
height: 100 height: 100
}, },
html: `<svg><rect x="0" y="0" width="100" height="100"></rect></svg>`, html: `<svg xmlns="http://www.w3.org/2000/svg"><rect x="0" y="0" width="100" height="100"></rect></svg>`,
test ( assert, component, target ) { test ( assert, component, target ) {
const svg = target.querySelector( 'svg' ); const svg = target.querySelector( 'svg' );
@ -18,6 +18,6 @@ export default {
assert.equal( rect.namespaceURI, 'http://www.w3.org/2000/svg' ); assert.equal( rect.namespaceURI, 'http://www.w3.org/2000/svg' );
component.set({ width: 150, height: 50 }); component.set({ width: 150, height: 50 });
assert.equal( target.innerHTML, `<svg><rect x="0" y="0" width="150" height="50"></rect></svg>` ); assert.equal( target.innerHTML, `<svg xmlns="http://www.w3.org/2000/svg"><rect x="0" y="0" width="150" height="50"></rect></svg>` );
} }
}; };

@ -65,6 +65,10 @@ function cleanChildren ( node ) {
} }
if ( child.nodeType === 3 ) { if ( child.nodeType === 3 ) {
if ( node.namespaceURI === 'http://www.w3.org/2000/svg' && node.tagName !== 'text' && node.tagName !== 'tspan' ) {
node.removeChild( child );
}
child.data = child.data.replace( /\s{2,}/, '\n' ); child.data = child.data.replace( /\s{2,}/, '\n' );
// text // text

@ -0,0 +1,23 @@
{
"html": {
"start": 0,
"end": 15,
"type": "Fragment",
"children": [{
"attributes": [{
"end": 14,
"name": "html",
"start": 10,
"type": "Attribute",
"value": true
}],
"children": [],
"end": 15,
"name": "!doctype",
"start": 0,
"type": "Element"
}]
},
"css": null,
"js": null
}

@ -1 +1 @@
&lt;p&gt;this should be &lt;em&gt;escaped&lt;/em&gt; &amp; so should &39;this&39;&lt;/p&gt; &lt;p&gt;this should be &lt;em&gt;escaped&lt;/em&gt; &amp; so should &#39;this&#39;&lt;/p&gt;

@ -1 +1 @@
&lt;p&gt;this should be &lt;em&gt;escaped&lt;/em&gt; &amp; so should &39;this&39;&lt;/p&gt; &lt;p&gt;this should be &lt;em&gt;escaped&lt;/em&gt; &amp; so should &#39;this&#39;&lt;/p&gt;

@ -0,0 +1,3 @@
{
"foo": "it's working"
}
Loading…
Cancel
Save