Merge branch 'master' into gh-592

pull/603/head
Rich Harris 8 years ago committed by GitHub
commit f511962325

3
.gitignore vendored

@ -12,4 +12,5 @@ coverage.lcov
test/sourcemaps/samples/*/output.js
test/sourcemaps/samples/*/output.js.map
_actual.*
_actual-bundle.*
_actual-bundle.*
src/generators/dom/shared.ts

@ -117,11 +117,7 @@ export default class Generator {
const { name } = flattenReference( node );
if ( scope.has( name ) ) return;
if ( parent && parent.type === 'CallExpression' && node === parent.callee && helpers.has( name ) ) {
code.prependRight( node.start, `${self.alias( 'template' )}.helpers.` );
}
else if ( name === 'event' && isEventHandler ) {
if ( name === 'event' && isEventHandler ) {
// noop
}
@ -135,6 +131,10 @@ export default class Generator {
if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name );
}
else if ( helpers.has( name ) ) {
code.prependRight( node.start, `${self.alias( 'template' )}.helpers.` );
}
else if ( indexes.has( name ) ) {
const context = indexes.get( name );
if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context );

@ -1,35 +0,0 @@
// this file is auto-generated, do not edit it
export default {
"appendNode": "function appendNode ( node, target ) {\n\ttarget.appendChild( node );\n}",
"insertNode": "function insertNode ( node, target, anchor ) {\n\ttarget.insertBefore( node, anchor );\n}",
"detachNode": "function detachNode ( node ) {\n\tnode.parentNode.removeChild( node );\n}",
"detachBetween": "function detachBetween ( before, after ) {\n\twhile ( before.nextSibling && before.nextSibling !== after ) {\n\t\tbefore.parentNode.removeChild( before.nextSibling );\n\t}\n}",
"destroyEach": "function destroyEach ( iterations, detach, start ) {\n\tfor ( var i = start; i < iterations.length; i += 1 ) {\n\t\tif ( iterations[i] ) iterations[i].destroy( detach );\n\t}\n}",
"createElement": "function createElement ( name ) {\n\treturn document.createElement( name );\n}",
"createSvgElement": "function createSvgElement ( name ) {\n\treturn document.createElementNS( 'http://www.w3.org/2000/svg', name );\n}",
"createText": "function createText ( data ) {\n\treturn document.createTextNode( data );\n}",
"createComment": "function createComment () {\n\treturn document.createComment( '' );\n}",
"addEventListener": "function addEventListener ( node, event, handler ) {\n\tnode.addEventListener( event, handler, false );\n}",
"removeEventListener": "function removeEventListener ( node, event, handler ) {\n\tnode.removeEventListener( event, handler, false );\n}",
"setAttribute": "function setAttribute ( node, attribute, value ) {\n\tnode.setAttribute( attribute, value );\n}",
"setXlinkAttribute": "function setXlinkAttribute ( node, attribute, value ) {\n\tnode.setAttributeNS( 'http://www.w3.org/1999/xlink', attribute, value );\n}",
"getBindingGroupValue": "function getBindingGroupValue ( group ) {\n\tvar value = [];\n\tfor ( var i = 0; i < group.length; i += 1 ) {\n\t\tif ( group[i].checked ) value.push( group[i].__value );\n\t}\n\treturn value;\n}",
"differs": "function differs ( a, b ) {\n\treturn ( a !== b ) || ( a && ( typeof a === 'object' ) || ( typeof a === 'function' ) );\n}",
"dispatchObservers": "function dispatchObservers ( component, group, newState, oldState ) {\n\tfor ( var key in group ) {\n\t\tif ( !( key in newState ) ) continue;\n\n\t\tvar newValue = newState[ key ];\n\t\tvar oldValue = oldState[ key ];\n\n\t\tif ( differs( newValue, oldValue ) ) {\n\t\t\tvar callbacks = group[ key ];\n\t\t\tif ( !callbacks ) continue;\n\n\t\t\tfor ( var i = 0; i < callbacks.length; i += 1 ) {\n\t\t\t\tvar callback = callbacks[i];\n\t\t\t\tif ( callback.__calling ) continue;\n\n\t\t\t\tcallback.__calling = true;\n\t\t\t\tcallback.call( component, newValue, oldValue );\n\t\t\t\tcallback.__calling = false;\n\t\t\t}\n\t\t}\n\t}\n}",
"get": "function get ( key ) {\n\treturn key ? this._state[ key ] : this._state;\n}",
"fire": "function fire ( eventName, data ) {\n\tvar handlers = eventName in this._handlers && this._handlers[ eventName ].slice();\n\tif ( !handlers ) return;\n\n\tfor ( var i = 0; i < handlers.length; i += 1 ) {\n\t\thandlers[i].call( this, data );\n\t}\n}",
"observe": "function observe ( key, callback, options ) {\n\tvar group = ( options && options.defer ) ? this._observers.post : this._observers.pre;\n\n\t( group[ key ] || ( group[ key ] = [] ) ).push( callback );\n\n\tif ( !options || options.init !== false ) {\n\t\tcallback.__calling = true;\n\t\tcallback.call( this, this._state[ key ] );\n\t\tcallback.__calling = false;\n\t}\n\n\treturn {\n\t\tcancel: function () {\n\t\t\tvar index = group[ key ].indexOf( callback );\n\t\t\tif ( ~index ) group[ key ].splice( index, 1 );\n\t\t}\n\t};\n}",
"observeDev": "function observeDev ( key, callback, options ) {\n\tvar c = ( key = '' + key ).search( /[^\\w]/ );\n\tif ( c > -1 ) {\n\t\tvar message = \"The first argument to component.observe(...) must be the name of a top-level property\";\n\t\tif ( c > 0 ) message += \", i.e. '\" + key.slice( 0, c ) + \"' rather than '\" + key + \"'\";\n\n\t\tthrow new Error( message );\n\t}\n\n\treturn observe.call( this, key, callback, options );\n}",
"on": "function on ( eventName, handler ) {\n\tif ( eventName === 'teardown' ) return this.on( 'destroy', handler );\n\n\tvar handlers = this._handlers[ eventName ] || ( this._handlers[ eventName ] = [] );\n\thandlers.push( handler );\n\n\treturn {\n\t\tcancel: function () {\n\t\t\tvar index = handlers.indexOf( handler );\n\t\t\tif ( ~index ) handlers.splice( index, 1 );\n\t\t}\n\t};\n}",
"onDev": "function onDev ( eventName, handler ) {\n\tif ( eventName === 'teardown' ) {\n\t\tconsole.warn( \"Use component.on('destroy', ...) instead of component.on('teardown', ...) which has been deprecated and will be unsupported in Svelte 2\" );\n\t\treturn this.on( 'destroy', handler );\n\t}\n\n\treturn on.call( this, eventName, handler );\n}",
"set": "function set ( newState ) {\n\tthis._set( assign( {}, newState ) );\n\tthis._root._flush();\n}",
"_flush": "function _flush () {\n\tif ( !this._renderHooks ) return;\n\n\twhile ( this._renderHooks.length ) {\n\t\tthis._renderHooks.pop()();\n\t}\n}",
"proto": "{\n\tget: get,\n\tfire: fire,\n\tobserve: observe,\n\ton: on,\n\tset: set,\n\t_flush: _flush\n}",
"protoDev": "{\n\tget: get,\n\tfire: fire,\n\tobserve: observeDev,\n\ton: onDev,\n\tset: set,\n\t_flush: _flush\n}",
"linear": "function linear ( t ) {\n\treturn t;\n}",
"generateKeyframes": "function generateKeyframes ( a, b, delta, duration, ease, fn, node, style ) {\n\tvar id = '__svelte' + ~~( Math.random() * 1e9 ); // TODO make this more robust\n\tvar keyframes = '@keyframes ' + id + '{\\n';\n\n\tfor ( var p = 0; p <= 1; p += 16.666 / duration ) {\n\t\tvar t = a + delta * ease( p );\n\t\tkeyframes += ( p * 100 ) + '%{' + fn( t ) + '}\\n';\n\t}\n\n\tkeyframes += '100% {' + fn( b ) + '}\\n}';\n\tstyle.textContent += keyframes;\n\n\tdocument.head.appendChild( style );\n\n\tnode.style.animation = ( node.style.animation || '' ).split( ',' )\n\t\t.filter( function ( anim ) {\n\t\t\t// when introing, discard old animations if there are any\n\t\t\treturn anim && ( delta < 0 || !/__svelte/.test( anim ) );\n\t\t})\n\t\t.concat( id + ' ' + duration + 'ms linear 1 forwards' )\n\t\t.join( ', ' );\n}",
"wrapTransition": "function wrapTransition ( node, fn, params, intro, outgroup ) {\n\tvar obj = fn( node, params );\n\tvar duration = obj.duration || 300;\n\tvar ease = obj.easing || linear;\n\n\t// TODO share <style> tag between all transitions?\n\tif ( obj.css ) {\n\t\tvar style = document.createElement( 'style' );\n\t}\n\n\tif ( intro && obj.tick ) obj.tick( 0 );\n\n\treturn {\n\t\tt: intro ? 0 : 1,\n\t\trunning: false,\n\t\tprogram: null,\n\t\tpending: null,\n\t\trun: function ( intro, callback ) {\n\t\t\tvar program = {\n\t\t\t\tstart: window.performance.now() + ( obj.delay || 0 ),\n\t\t\t\tintro: intro,\n\t\t\t\tcallback: callback\n\t\t\t};\n\n\t\t\tif ( obj.delay ) {\n\t\t\t\tthis.pending = program;\n\t\t\t} else {\n\t\t\t\tthis.start( program );\n\t\t\t}\n\n\t\t\tif ( !this.running ) {\n\t\t\t\tthis.running = true;\n\t\t\t\ttransitionManager.add( this );\n\t\t\t}\n\t\t},\n\t\tstart: function ( program ) {\n\t\t\tprogram.a = this.t;\n\t\t\tprogram.b = program.intro ? 1 : 0;\n\t\t\tprogram.delta = program.b - program.a;\n\t\t\tprogram.duration = duration * Math.abs( program.b - program.a );\n\t\t\tprogram.end = program.start + program.duration;\n\n\t\t\tif ( obj.css ) {\n\t\t\t\tgenerateKeyframes( program.a, program.b, program.delta, program.duration, ease, obj.css, node, style );\n\t\t\t}\n\n\t\t\tthis.program = program;\n\t\t\tthis.pending = null;\n\t\t},\n\t\tupdate: function ( now ) {\n\t\t\tvar program = this.program;\n\t\t\tif ( !program ) return;\n\n\t\t\tvar p = now - program.start;\n\t\t\tthis.t = program.a + program.delta * ease( p / program.duration );\n\t\t\tif ( obj.tick ) obj.tick( this.t );\n\t\t},\n\t\tdone: function () {\n\t\t\tthis.t = this.program.b;\n\t\t\tif ( obj.tick ) obj.tick( this.t );\n\t\t\tif ( obj.css ) document.head.removeChild( style );\n\t\t\tthis.program.callback();\n\t\t\tthis.program = null;\n\t\t\tthis.running = !!this.pending;\n\t\t},\n\t\tabort: function () {\n\t\t\tif ( obj.tick ) obj.tick( 1 );\n\t\t\tif ( obj.css ) document.head.removeChild( style );\n\t\t\tthis.program = this.pending = null;\n\t\t\tthis.running = false;\n\t\t}\n\t};\n}",
"transitionManager": "{\n\trunning: false,\n\ttransitions: [],\n\tbound: null,\n\n\tadd: function ( transition ) {\n\t\tthis.transitions.push( transition );\n\n\t\tif ( !this.running ) {\n\t\t\tthis.running = true;\n\t\t\tthis.next();\n\t\t}\n\t},\n\n\tnext: function () {\n\t\tthis.running = false;\n\n\t\tvar now = window.performance.now();\n\t\tvar i = this.transitions.length;\n\n\t\twhile ( i-- ) {\n\t\t\tvar transition = this.transitions[i];\n\n\t\t\tif ( transition.program && now >= transition.program.end ) {\n\t\t\t\ttransition.done();\n\t\t\t}\n\n\t\t\tif ( transition.pending && now >= transition.pending.start ) {\n\t\t\t\ttransition.start( transition.pending );\n\t\t\t}\n\n\t\t\tif ( transition.running ) {\n\t\t\t\ttransition.update( now );\n\t\t\t\tthis.running = true;\n\t\t\t} else if ( !transition.pending ) {\n\t\t\t\tthis.transitions.splice( i, 1 );\n\t\t\t}\n\t\t}\n\n\t\tif ( this.running ) {\n\t\t\trequestAnimationFrame( this.bound || ( this.bound = this.next.bind( this ) ) );\n\t\t}\n\t}\n}",
"noop": "function noop () {}",
"assign": "function assign ( target ) {\n\tfor ( var i = 1; i < arguments.length; i += 1 ) {\n\t\tvar source = arguments[i];\n\t\tfor ( var k in source ) target[k] = source[k];\n\t}\n\n\treturn target;\n}"
};

@ -107,6 +107,20 @@ export default function visitElement ( generator: DomGenerator, block: Block, st
}
if ( node.name !== 'select' ) {
if ( node.name === 'textarea' ) {
// this is an egregious hack, but it's the easiest way to get <textarea>
// children treated the same way as a value attribute
if ( node.children.length > 0 ) {
node.attributes.push({
type: 'Attribute',
name: 'value',
value: node.children
});
node.children = [];
}
}
// <select> value attributes are an annoying special case — it must be handled
// *after* its children have been updated
visitAttributesAndAddProps();

@ -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', 'select' ] },
value: { appliesTo: [ 'button', 'option', 'input', 'li', 'meter', 'progress', 'param', 'select', 'textarea' ] },
width: { appliesTo: [ 'canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video' ] },
wrap: { appliesTo: [ 'textarea' ] }
};

@ -10,6 +10,17 @@ const meta = {
':Window': visitWindow
};
function stringifyAttributeValue ( block: Block, chunks: Node[] ) {
return chunks.map( ( chunk: Node ) => {
if ( chunk.type === 'Text' ) {
return chunk.data;
}
const { snippet } = block.contextualise( chunk.expression );
return '${' + snippet + '}';
}).join( '' )
}
export default function visitElement ( generator: SsrGenerator, block: Block, node: Node ) {
if ( node.name in meta ) {
return meta[ node.name ]( generator, block, node );
@ -21,24 +32,22 @@ export default function visitElement ( generator: SsrGenerator, block: Block, no
}
let openingTag = `<${node.name}`;
let textareaContents; // awkward special case
node.attributes.forEach( ( attribute: Node ) => {
if ( attribute.type !== 'Attribute' ) return;
let str = ` ${attribute.name}`;
if ( attribute.name === 'value' && node.name === 'textarea' ) {
textareaContents = stringifyAttributeValue( block, attribute.value );
} else {
let str = ` ${attribute.name}`;
if ( attribute.value !== true ) {
str += `="` + attribute.value.map( ( chunk: Node ) => {
if ( chunk.type === 'Text' ) {
return chunk.data;
}
if ( attribute.value !== true ) {
str += `="${stringifyAttributeValue( block, attribute.value )}"`;
}
const { snippet } = block.contextualise( chunk.expression );
return '${' + snippet + '}';
}).join( '' ) + `"`;
openingTag += str;
}
openingTag += str;
});
if ( generator.cssId && !generator.elementDepth ) {
@ -49,13 +58,17 @@ export default function visitElement ( generator: SsrGenerator, block: Block, no
generator.append( openingTag );
generator.elementDepth += 1;
if ( node.name === 'textarea' && textareaContents !== undefined ) {
generator.append( textareaContents );
} else {
generator.elementDepth += 1;
node.children.forEach( ( child: Node ) => {
visit( generator, block, child );
});
node.children.forEach( ( child: Node ) => {
visit( generator, block, child );
});
generator.elementDepth -= 1;
generator.elementDepth -= 1;
}
if ( !isVoidElementName( node.name ) ) {
generator.append( `</${node.name}>` );

@ -9,7 +9,6 @@ import { Parser } from '../index';
import { Node } from '../../interfaces';
const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const invalidUnquotedAttributeCharacters = /[\s"'=<>\/`]/;
const SELF = ':Self';
@ -181,6 +180,11 @@ export default function tag ( parser: Parser ) {
if ( selfClosing ) {
element.end = parser.index;
} else if ( name === 'textarea' ) {
// special case
element.children = readSequence( parser, () => parser.template.slice( parser.index, parser.index + 11 ) === '</textarea>' );
parser.read( /<\/textarea>/ );
element.end = parser.index;
} else {
// don't push self-closing elements onto the stack
parser.stack.push( element );
@ -280,11 +284,41 @@ function readAttribute ( parser: Parser, uniqueNames ) {
}
function readAttributeValue ( parser: Parser ) {
let quoteMark;
const quoteMark = (
parser.eat( `'` ) ? `'` :
parser.eat( `"` ) ? `"` :
null
);
const regex = (
quoteMark === `'` ? /'/ :
quoteMark === `"` ? /"/ :
/[\s"'=<>\/`]/
);
const value = readSequence( parser, () => regex.test( parser.template[ parser.index ] ) );
if ( quoteMark ) parser.index += 1;
return value;
}
if ( parser.eat( `'` ) ) quoteMark = `'`;
if ( parser.eat( `"` ) ) quoteMark = `"`;
function getShorthandValue ( start: number, name: string ) {
const end = start + name.length;
return [{
type: 'AttributeShorthand',
start,
end,
expression: {
type: 'Identifier',
start,
end,
name
}
}];
}
function readSequence ( parser: Parser, done: () => boolean ) {
let currentChunk: Node = {
start: parser.index,
end: null,
@ -292,16 +326,24 @@ function readAttributeValue ( parser: Parser ) {
data: ''
};
const done = quoteMark ?
char => char === quoteMark :
char => invalidUnquotedAttributeCharacters.test( char );
const chunks = [];
while ( parser.index < parser.template.length ) {
const index = parser.index;
if ( parser.eat( '{{' ) ) {
if ( done() ) {
currentChunk.end = parser.index;
if ( currentChunk.data ) chunks.push( currentChunk );
chunks.forEach( chunk => {
if ( chunk.type === 'Text' ) chunk.data = decodeCharacterReferences( chunk.data );
});
return chunks;
}
else if ( parser.eat( '{{' ) ) {
if ( currentChunk.data ) {
currentChunk.end = index;
chunks.push( currentChunk );
@ -328,39 +370,10 @@ function readAttributeValue ( parser: Parser ) {
};
}
else if ( done( parser.template[ parser.index ] ) ) {
currentChunk.end = parser.index;
if ( quoteMark ) parser.index += 1;
if ( currentChunk.data ) chunks.push( currentChunk );
chunks.forEach( chunk => {
if ( chunk.type === 'Text' ) chunk.data = decodeCharacterReferences( chunk.data );
});
return chunks;
}
else {
currentChunk.data += parser.template[ parser.index++ ];
}
}
parser.error( `Unexpected end of input` );
}
function getShorthandValue ( start: number, name: string ) {
const end = start + name.length;
return [{
type: 'AttributeShorthand',
start,
end,
expression: {
type: 'Identifier',
start,
end,
name
}
}];
}
}

@ -31,13 +31,21 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) {
var obj = fn( node, params );
var duration = obj.duration || 300;
var ease = obj.easing || linear;
var cssText;
// TODO share <style> tag between all transitions?
if ( obj.css ) {
var style = document.createElement( 'style' );
}
if ( intro && obj.tick ) obj.tick( 0 );
if ( intro ) {
if ( obj.css && obj.delay ) {
cssText = node.style.cssText;
node.style.cssText += obj.css( 0 );
}
if ( obj.tick ) obj.tick( 0 );
}
return {
t: intro ? 0 : 1,
@ -70,6 +78,7 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) {
program.end = program.start + program.duration;
if ( obj.css ) {
if ( obj.delay ) node.style.cssText = cssText;
generateKeyframes( program.a, program.b, program.delta, program.duration, ease, obj.css, node, style );
}

@ -26,6 +26,17 @@ export default function validateHtml ( validator: Validator, html: Node ) {
elementDepth += 1;
validateElement( validator, node );
} else if ( node.type === 'EachBlock' ) {
if ( validator.helpers.has( node.context ) ) {
let c = node.expression.end;
// find start of context
while ( /\s/.test( validator.source[c] ) ) c += 1;
c += 2;
while ( /\s/.test( validator.source[c] ) ) c += 1;
validator.warn( `Context clashes with a helper. Rename one or the other to eliminate any ambiguity`, c );
}
}
if ( node.children ) {

@ -77,6 +77,14 @@ export default function validateElement ( validator: Validator, node: Node ) {
validator.error( `Missing transition '${attribute.name}'`, attribute.start );
}
}
else if ( attribute.type === 'Attribute' ) {
if ( attribute.name === 'value' && node.name === 'textarea' ) {
if ( node.children.length ) {
validator.error( `A <textarea> can have either a value attribute or (equivalently) child content, but not both`, attribute.start );
}
}
}
});
}

@ -0,0 +1,3 @@
<textarea>
<p>not actually an element. {{foo}}</p>
</textarea>

@ -0,0 +1,44 @@
{
"hash": 3618147195,
"html": {
"start": 0,
"end": 63,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 63,
"type": "Element",
"name": "textarea",
"attributes": [],
"children": [
{
"start": 10,
"end": 40,
"type": "Text",
"data": "\n\t<p>not actually an element. "
},
{
"start": 40,
"end": 47,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 42,
"end": 45,
"name": "foo"
}
},
{
"start": 47,
"end": 52,
"type": "Text",
"data": "</p>\n"
}
]
}
]
},
"css": null,
"js": null
}

@ -0,0 +1,3 @@
export default {
html: '<p>1,4,9</p>'
};

@ -0,0 +1,15 @@
<p>{{numbers.map(square)}}</p>
<script>
export default {
data () {
return {
numbers: [ 1, 2, 3 ]
};
},
helpers: {
square: num => num * num
}
};
</script>

@ -0,0 +1,17 @@
export default {
'skip-ssr': true, // SSR behaviour is awkwardly different
data: {
foo: 42
},
html: `<textarea></textarea>`,
test ( assert, component, target ) {
const textarea = target.querySelector( 'textarea' );
assert.strictEqual( textarea.value, `\n\t<p>not actually an element. 42</p>\n` );
component.set({ foo: 43 });
assert.strictEqual( textarea.value, `\n\t<p>not actually an element. 43</p>\n` );
}
};

@ -0,0 +1,3 @@
<textarea>
<p>not actually an element. {{foo}}</p>
</textarea>

@ -0,0 +1,17 @@
export default {
'skip-ssr': true, // SSR behaviour is awkwardly different
data: {
foo: 42
},
html: `<textarea></textarea>`,
test ( assert, component, target ) {
const textarea = target.querySelector( 'textarea' );
assert.strictEqual( textarea.value, '42' );
component.set({ foo: 43 });
assert.strictEqual( textarea.value, '43' );
}
};

@ -0,0 +1 @@
<textarea value='{{foo}}'/>

@ -0,0 +1,12 @@
export default {
test ( assert, component, target, window, raf ) {
component.set({ visible: true });
const div = target.querySelector( 'div' );
assert.strictEqual( div.style.opacity, '0' );
raf.tick( 50 );
assert.strictEqual( div.style.opacity, '' );
component.destroy();
}
};

@ -0,0 +1,19 @@
{{#if visible}}
<div transition:foo>delayed</div>
{{/if}}
<script>
export default {
transitions: {
foo: function ( node, params ) {
return {
delay: 50,
duration: 100,
css: t => {
return `opacity: ${t}`;
}
};
}
}
};
</script>

@ -0,0 +1,3 @@
<textarea>
<p>not actually an element. 42</p>
</textarea>

@ -0,0 +1,11 @@
<textarea>
<p>not actually an element. {{foo}}</p>
</textarea>
<script>
export default {
data () {
return { foo: 42 };
}
};
</script>

@ -0,0 +1,9 @@
<textarea value='{{foo}}'/>
<script>
export default {
data () {
return { foo: 42 };
}
};
</script>

@ -0,0 +1,17 @@
{{#each things as thing}}
{{thing}}
{{/each}}
<script>
export default {
data () {
return { things: [ 'a', 'b', 'c' ] };
},
helpers: {
thing: function ( x ) {
return x;
}
}
};
</script>

@ -0,0 +1,8 @@
[{
"message": "Context clashes with a helper. Rename one or the other to eliminate any ambiguity",
"loc": {
"line": 1,
"column": 18
},
"pos": 18
}]

@ -0,0 +1,8 @@
[{
"message": "A <textarea> can have either a value attribute or (equivalently) child content, but not both",
"loc": {
"line": 1,
"column": 10
},
"pos": 10
}]

@ -0,0 +1,3 @@
<textarea value='{{foo}}'>
some illegal text
</textarea>
Loading…
Cancel
Save