mirror of
https://github.com/sveltejs/svelte.git
synced 2024-12-01 17:30:59 +01:00
commit
f699b610a8
@ -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)
|
||||
|
@ -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' )}
|
||||
}
|
||||
|
@ -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 );` );
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ export default {
|
||||
|
||||
return `var ${contextName} = ${listName}[${indexName}];`;
|
||||
}).join( '\n' ) ],
|
||||
detachStatements: [],
|
||||
teardownStatements: [],
|
||||
|
||||
counter: counter(),
|
||||
|
@ -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 );
|
||||
|
@ -13,6 +13,7 @@ function generateBlock ( generator, node, name ) {
|
||||
initStatements: [],
|
||||
mountStatements: [],
|
||||
updateStatements: [],
|
||||
detachStatements: [],
|
||||
teardownStatements: [],
|
||||
|
||||
counter: counter()
|
||||
|
7
compiler/generate/visitors/YieldTag.js
Normal file
7
compiler/generate/visitors/YieldTag.js
Normal 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 );`);
|
||||
}
|
||||
};
|
@ -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
|
||||
};
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 ) {
|
||||
|
15
test/compiler/component-yield-if/Widget.html
Normal file
15
test/compiler/component-yield-if/Widget.html
Normal file
@ -0,0 +1,15 @@
|
||||
<p>
|
||||
{{#if show}}
|
||||
{{yield}}
|
||||
{{/if}}
|
||||
</p>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
24
test/compiler/component-yield-if/_config.js
Normal file
24
test/compiler/component-yield-if/_config.js
Normal 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>' );
|
||||
}
|
||||
};
|
14
test/compiler/component-yield-if/main.html
Normal file
14
test/compiler/component-yield-if/main.html
Normal 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>
|
1
test/compiler/component-yield-parent/Widget.html
Normal file
1
test/compiler/component-yield-parent/Widget.html
Normal file
@ -0,0 +1 @@
|
||||
<p>{{yield}}</p>
|
9
test/compiler/component-yield-parent/_config.js
Normal file
9
test/compiler/component-yield-parent/_config.js
Normal 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>' );
|
||||
}
|
||||
}
|
14
test/compiler/component-yield-parent/main.html
Normal file
14
test/compiler/component-yield-parent/main.html
Normal 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>
|
3
test/compiler/component-yield/_config.js
Normal file
3
test/compiler/component-yield/_config.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
html: '<p>Hello</p>'
|
||||
}
|
15
test/compiler/component-yield/main.html
Normal file
15
test/compiler/component-yield/main.html
Normal file
@ -0,0 +1,15 @@
|
||||
<p>
|
||||
Hello
|
||||
{{#if test}}
|
||||
{{yield}}
|
||||
{{/if}}
|
||||
</p>
|
||||
<script>
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
test: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
1
test/parser/yield/input.html
Normal file
1
test/parser/yield/input.html
Normal file
@ -0,0 +1 @@
|
||||
{{yield}}
|
14
test/parser/yield/output.json
Normal file
14
test/parser/yield/output.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 9,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 9,
|
||||
"type": "YieldTag"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
31
test/test.js
31
test/test.js
@ -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;
|
||||
});
|
||||
});
|
||||
|
8
test/validator/computed-values/errors.json
Normal file
8
test/validator/computed-values/errors.json
Normal file
@ -0,0 +1,8 @@
|
||||
[{
|
||||
"message": "A computed value must depend on at least one property",
|
||||
"pos": 49,
|
||||
"loc": {
|
||||
"line": 4,
|
||||
"column": 8
|
||||
}
|
||||
}]
|
7
test/validator/computed-values/input.html
Normal file
7
test/validator/computed-values/input.html
Normal file
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
foo: () => {}
|
||||
}
|
||||
};
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user