0
0
mirror of https://github.com/sveltejs/svelte.git synced 2024-12-01 17:30:59 +01:00

add changes from #112 to #111

This commit is contained in:
Rich-Harris 2016-12-04 21:19:47 -05:00
commit f699b610a8
23 changed files with 241 additions and 37 deletions

View File

@ -43,13 +43,19 @@ const { code, map } = svelte.compile( source, {
console.error( err.message );
},
onwarning: warning => {
onwarn: warning => {
console.warn( warning.message );
}
});
```
## Example/starter repos
* [charpeni/svelte-example](https://github.com/charpeni/svelte-example) - Some Svelte examples with configured Rollup, Babel, ESLint, directives, Two-Way binding, and nested components
* [EmilTholin/svelte-test](https://github.com/EmilTholin/svelte-test)
## License
[MIT](LICENSE)

View File

@ -27,8 +27,8 @@ export default function generate ( parsed, source, options ) {
` );
}
if ( isToplevel ) {
generator.current.teardownStatements.push( deindent`
if ( detach ) ${name}.parentNode.removeChild( ${name} );
generator.current.detachStatements.push( deindent`
${name}.parentNode.removeChild( ${name} );
` );
}
},
@ -72,6 +72,10 @@ export default function generate ( parsed, source, options ) {
teardown: function ( detach ) {
${fragment.teardownStatements.join( '\n\n' )}
if ( detach ) {
${fragment.detachStatements.join( '\n\n' )}
}
}
};
}
@ -255,6 +259,7 @@ export default function generate ( parsed, source, options ) {
initStatements: [],
mountStatements: [],
updateStatements: [],
detachStatements: [],
teardownStatements: [],
contexts: {},
@ -516,6 +521,7 @@ export default function generate ( parsed, source, options ) {
};
this.root = options.root;
this.yield = options.yield;
${initStatements.join( '\n\n' )}
}

View File

@ -1,8 +1,10 @@
import deindent from '../utils/deindent.js';
import addComponentAttributes from './attributes/addComponentAttributes.js';
import counter from '../utils/counter.js';
export default {
enter ( generator, node ) {
const hasChildren = node.children.length > 0;
const name = generator.current.counter( `${node.name[0].toLowerCase()}${node.name.slice( 1 )}` );
const local = {
@ -24,13 +26,49 @@ export default {
addComponentAttributes( generator, node, local );
const componentInitProperties = [
`target: ${!isToplevel ? generator.current.target: 'null'}`,
'root: component.root || component'
];
// Component has children
if ( hasChildren ) {
const yieldName = `render${name}YieldFragment`;
// {{YIELD STUFF}}
generator.push({
useAnchor: true,
name: generator.current.counter(yieldName),
target: 'target',
localElementDepth: 0,
initStatements: [],
mountStatements: [],
updateStatements: [],
teardownStatements: [],
counter: counter()
});
node.children.forEach( generator.visit );
generator.addRenderer( generator.current );
generator.pop();
// Don't render children twice
node.children = [];
generator.current.initStatements.push(`var ${name}_yieldFragment = ${yieldName}( root, component );`);
generator.current.updateStatements.push(`${name}_yieldFragment.update ( changed, root );`);
componentInitProperties.push(`yield: ${name}_yieldFragment`);
}
const statements = [];
if ( local.staticAttributes.length || local.dynamicAttributes.length || local.bindings.length ) {
const initialProps = local.staticAttributes
.concat( local.dynamicAttributes )
.map( attribute => `${attribute.name}: ${attribute.value}` );
const statements = [];
if ( initialProps.length ) {
statements.push( deindent`
var ${name}_initialData = {
@ -50,25 +88,16 @@ export default {
statements.push( bindings.join( '\n' ) );
}
local.init.unshift( deindent`
${statements.join( '\n\n' )}
var ${name} = new template.components.${node.name}({
target: ${!isToplevel ? generator.current.target: 'null'},
root: component.root || component,
data: ${name}_initialData
});
` );
} else {
local.init.unshift( deindent`
var ${name} = new template.components.${node.name}({
target: ${!isToplevel ? generator.current.target: 'null'},
root: component.root || component
});
` );
componentInitProperties.push(`data: ${name}_initialData`);
}
local.init.unshift( deindent`
${statements.join( '\n\n' )}
var ${name} = new template.components.${node.name}({
${componentInitProperties.join(',\n')}
});
` );
if ( isToplevel ) {
local.mount.unshift( `${name}.mount( target, anchor );` );
}

View File

@ -102,6 +102,7 @@ export default {
return `var ${contextName} = ${listName}[${indexName}];`;
}).join( '\n' ) ],
detachStatements: [],
teardownStatements: [],
counter: counter(),

View File

@ -21,6 +21,7 @@ export default {
init: [],
mount: [],
update: [],
detach: [],
teardown: []
};
@ -81,6 +82,7 @@ export default {
generator.current.initStatements.push( local.init.join( '\n' ) );
if ( local.update.length ) generator.current.updateStatements.push( local.update.join( '\n' ) );
if ( local.mount.length ) generator.current.mountStatements.push( local.mount.join( '\n' ) );
generator.current.detachStatements.push( local.detach.join( '\n' ) );
generator.current.teardownStatements.push( local.teardown.join( '\n' ) );
generator.createMountStatement( name );

View File

@ -13,6 +13,7 @@ function generateBlock ( generator, node, name ) {
initStatements: [],
mountStatements: [],
updateStatements: [],
detachStatements: [],
teardownStatements: [],
counter: counter()

View File

@ -0,0 +1,7 @@
export default {
enter ( generator ) {
const anchor = generator.createAnchor( 'yield', 'yield' );
generator.current.mountStatements.push(`component.yield && component.yield.mount( ${generator.current.target}, ${anchor} );`);
generator.current.teardownStatements.push(`component.yield && component.yield.teardown( detach );`);
}
};

View File

@ -4,6 +4,7 @@ import Element from './Element.js';
import IfBlock from './IfBlock.js';
import MustacheTag from './MustacheTag.js';
import Text from './Text.js';
import YieldTag from './YieldTag.js';
export default {
Comment,
@ -11,5 +12,6 @@ export default {
Element,
IfBlock,
MustacheTag,
Text
Text,
YieldTag
};

View File

@ -177,6 +177,19 @@ export default function mustache ( parser ) {
parser.stack.push( block );
}
// {{yield}}
else if ( parser.eat( 'yield' ) ) {
parser.allowWhitespace();
parser.eat( '}}', true );
parser.current().children.push({
start,
end: parser.index,
type: 'YieldTag'
});
}
else {
const expression = readExpression( parser );

View File

@ -21,7 +21,14 @@ export default function computed ( validator, prop ) {
return;
}
computation.value.params.forEach( param => {
const params = computation.value.params;
if ( params.length === 0 ) {
validator.error( `A computed value must depend on at least one property`, computation.value.start );
return;
}
params.forEach( param => {
const valid = param.type === 'Identifier' || param.type === 'AssignmentPattern' && param.left.type === 'Identifier';
if ( !valid ) {

View File

@ -0,0 +1,15 @@
<p>
{{#if show}}
{{yield}}
{{/if}}
</p>
<script>
export default {
data () {
return {
show: false
}
}
};
</script>

View File

@ -0,0 +1,24 @@
export default {
html: '<div><p></p></div>',
test ( assert, component, target ) {
const widget = component.refs.widget;
assert.equal( widget.get( 'show' ), false );
widget.set({show: true});
assert.htmlEqual( target.innerHTML, '<div><p>Hello</p></div>' );
component.set({data: 'World'});
assert.htmlEqual( target.innerHTML, '<div><p>World</p></div>' );
widget.set({show: false});
assert.htmlEqual( target.innerHTML, '<div><p></p></div>' );
component.set({data: 'Goodbye'});
assert.htmlEqual( target.innerHTML, '<div><p></p></div>' );
widget.set({show: true});
assert.htmlEqual( target.innerHTML, '<div><p>Goodbye</p></div>' );
}
};

View File

@ -0,0 +1,14 @@
<div>
<Widget ref:widget>{{data}}</Widget>
</div>
<script>
import Widget from './Widget.html'
export default {
components: { Widget },
data(){
return {
data: "Hello"
}
}
}
</script>

View File

@ -0,0 +1 @@
<p>{{yield}}</p>

View File

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

View File

@ -0,0 +1,14 @@
<div>
<Widget>{{data}}</Widget>
</div>
<script>
import Widget from './Widget.html'
export default {
components: { Widget },
data(){
return {
data: "Hello"
}
}
}
</script>

View File

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

View File

@ -0,0 +1,15 @@
<p>
Hello
{{#if test}}
{{yield}}
{{/if}}
</p>
<script>
export default {
data(){
return {
test: true
}
}
}
</script>

View File

@ -0,0 +1 @@
{{yield}}

View File

@ -0,0 +1,14 @@
{
"html": {
"start": 0,
"end": 9,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 9,
"type": "YieldTag"
}
]
}
}

View File

@ -22,8 +22,12 @@ const svelte = process.env.COVERAGE ?
const cache = {};
let showCompiledCode = false;
require.extensions[ '.html' ] = function ( module, filename ) {
const code = cache[ filename ] || ( cache[ filename ] = svelte.compile( fs.readFileSync( filename, 'utf-8' ) ).code );
if ( showCompiledCode ) console.log( addLineNumbers( code ) ); // eslint-disable-line no-console
return module._compile( code, filename );
};
@ -49,6 +53,15 @@ function env () {
});
}
function addLineNumbers ( code ) {
return code.split( '\n' ).map( ( line, i ) => {
i = String( i + 1 );
while ( i.length < 3 ) i = ` ${i}`;
return `${i}: ${line.replace( /^\t+/, match => match.split( '\t' ).join( ' ' ) )}`;
}).join( '\n' );
}
describe( 'svelte', () => {
before( () => {
function cleanChildren ( node ) {
@ -225,6 +238,8 @@ describe( 'svelte', () => {
( config.skip ? it.skip : config.solo ? it.only : it )( dir, () => {
let compiled;
showCompiledCode = config.show;
try {
const source = fs.readFileSync( `test/compiler/${dir}/main.html`, 'utf-8' );
compiled = svelte.compile( source );
@ -238,12 +253,6 @@ describe( 'svelte', () => {
}
const { code } = compiled;
const withLineNumbers = code.split( '\n' ).map( ( line, i ) => {
i = String( i + 1 );
while ( i.length < 3 ) i = ` ${i}`;
return `${i}: ${line.replace( /^\t+/, match => match.split( '\t' ).join( ' ' ) )}`;
}).join( '\n' );
// check that no ES2015+ syntax slipped in
try {
@ -251,7 +260,7 @@ describe( 'svelte', () => {
const es5 = spaces( startIndex ) + code.slice( startIndex ).replace( /export default .+/, '' );
acorn.parse( es5, { ecmaVersion: 5 });
} catch ( err ) {
console.log( withLineNumbers ); // eslint-disable-line no-console
if ( !config.show ) console.log( addLineNumbers( code ) ); // eslint-disable-line no-console
throw err;
}
@ -262,14 +271,10 @@ describe( 'svelte', () => {
try {
SvelteComponent = require( `./compiler/${dir}/main.html` ).default;
} catch ( err ) {
console.log( withLineNumbers ); // eslint-disable-line no-console
if ( !config.show ) console.log( addLineNumbers( code ) ); // eslint-disable-line no-console
throw err;
}
if ( config.show ) {
console.log( withLineNumbers ); // eslint-disable-line no-console
}
return env()
.then( window => {
const target = window.document.querySelector( 'main' );
@ -291,7 +296,7 @@ describe( 'svelte', () => {
}
})
.catch( err => {
if ( !config.show ) console.log( withLineNumbers ); // eslint-disable-line no-console
if ( !config.show ) console.log( addLineNumbers( code ) ); // eslint-disable-line no-console
throw err;
});
});

View File

@ -0,0 +1,8 @@
[{
"message": "A computed value must depend on at least one property",
"pos": 49,
"loc": {
"line": 4,
"column": 8
}
}]

View File

@ -0,0 +1,7 @@
<script>
export default {
computed: {
foo: () => {}
}
};
</script>