mirror of https://github.com/sveltejs/svelte
commit
d0c696bb2b
@ -0,0 +1,200 @@
|
|||||||
|
import deindent from '../../utils/deindent';
|
||||||
|
import { DomGenerator } from '../dom/index';
|
||||||
|
import Node from './shared/Node';
|
||||||
|
import Element from './Element';
|
||||||
|
import Block from '../dom/Block';
|
||||||
|
|
||||||
|
export default class Spread {
|
||||||
|
type: 'Spread';
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
|
||||||
|
generator: DomGenerator;
|
||||||
|
parent: Element;
|
||||||
|
expression: Node;
|
||||||
|
|
||||||
|
metadata: {
|
||||||
|
dependencies: string[];
|
||||||
|
snippet: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
generator,
|
||||||
|
expression,
|
||||||
|
parent
|
||||||
|
}: {
|
||||||
|
generator: DomGenerator,
|
||||||
|
expression: Node,
|
||||||
|
parent: Element
|
||||||
|
}) {
|
||||||
|
this.type = 'Spread';
|
||||||
|
this.generator = generator;
|
||||||
|
this.parent = parent;
|
||||||
|
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderForElement(block: Block) {
|
||||||
|
const node = this.parent;
|
||||||
|
|
||||||
|
const { expression } = this;
|
||||||
|
const { indexes } = block.contextualise(expression);
|
||||||
|
const { dependencies, snippet } = this.metadata;
|
||||||
|
|
||||||
|
const value = snippet;
|
||||||
|
|
||||||
|
const hasChangeableIndex = Array.from(indexes).some(index => block.changeableIndexes.get(index));
|
||||||
|
|
||||||
|
const shouldCache = (
|
||||||
|
expression.type !== 'Identifier' ||
|
||||||
|
block.contexts.has(expression.name) ||
|
||||||
|
hasChangeableIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
const last = shouldCache && block.getUniqueName(`${node.var}_spread_value`);
|
||||||
|
|
||||||
|
if (shouldCache) block.addVariable(last);
|
||||||
|
|
||||||
|
const init = shouldCache ? `${last} = ${value}` : value;
|
||||||
|
|
||||||
|
const activeKeys = block.getUniqueName(`${node.var}_spread_keys`);
|
||||||
|
block.addVariable(activeKeys, '{}');
|
||||||
|
|
||||||
|
const changes = block.getUniqueName(`${node.var}_spread_changes`);
|
||||||
|
|
||||||
|
const hasNamedAttributes = node.attributes.length;
|
||||||
|
const namedAttributes = block.getUniqueName(`${node.var}_attributes`);
|
||||||
|
|
||||||
|
if (hasNamedAttributes) {
|
||||||
|
block.builders.init.addBlock(deindent`
|
||||||
|
var ${namedAttributes} = [${node.attributes.map(attr => `'${attr.name}'`).join(', ')}];
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
block.builders.hydrate.addBlock(deindent`
|
||||||
|
var ${changes} = ${init};
|
||||||
|
for (var key in ${changes}) {
|
||||||
|
${hasNamedAttributes ? `if (${namedAttributes}.indexOf(key) !== -1) continue;` : ''}
|
||||||
|
|
||||||
|
@setAttribute(${node.var}, key, ${changes}[key]);
|
||||||
|
${activeKeys}[key] = true;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
if (dependencies.length || hasChangeableIndex) {
|
||||||
|
const changedCheck = (
|
||||||
|
( block.hasOutroMethod ? `#outroing || ` : '' ) +
|
||||||
|
dependencies.map(dependency => `changed.${dependency}`).join(' || ')
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateCachedValue = `${last} !== (${last} = ${value})`;
|
||||||
|
|
||||||
|
const condition = shouldCache ?
|
||||||
|
( dependencies.length ? `(${changedCheck}) && ${updateCachedValue}` : updateCachedValue ) :
|
||||||
|
changedCheck;
|
||||||
|
|
||||||
|
const oldKeys = block.getUniqueName(`${node.var}_spread_keys_old`);
|
||||||
|
|
||||||
|
const updater = deindent`
|
||||||
|
var ${oldKeys} = ${activeKeys};
|
||||||
|
${activeKeys} = {};
|
||||||
|
|
||||||
|
var ${changes} = ${shouldCache ? last : value};
|
||||||
|
for (var key in ${changes}) {
|
||||||
|
${hasNamedAttributes ? `if (${namedAttributes}.indexOf(key) !== -1) continue;` : ''}
|
||||||
|
|
||||||
|
@setAttribute(${node.var}, key, ${changes}[key]);
|
||||||
|
|
||||||
|
${activeKeys}[key] = true;
|
||||||
|
delete ${oldKeys}[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var key in ${oldKeys}) {
|
||||||
|
@removeAttribute(${node.var}, key);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
block.builders.update.addConditional(
|
||||||
|
condition,
|
||||||
|
updater
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderForComponent(block: Block, updates: string[]) {
|
||||||
|
const node = this.parent;
|
||||||
|
|
||||||
|
|
||||||
|
const { expression } = this;
|
||||||
|
const { indexes } = block.contextualise(expression);
|
||||||
|
const { dependencies, snippet } = this.metadata;
|
||||||
|
|
||||||
|
const value = snippet;
|
||||||
|
|
||||||
|
const hasChangeableIndex = Array.from(indexes).some(index => block.changeableIndexes.get(index));
|
||||||
|
|
||||||
|
const shouldCache = (
|
||||||
|
expression.type !== 'Identifier' ||
|
||||||
|
block.contexts.has(expression.name) ||
|
||||||
|
hasChangeableIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
const last = shouldCache && block.getUniqueName(`${node.var}_spread_value`);
|
||||||
|
|
||||||
|
if (shouldCache) block.addVariable(last);
|
||||||
|
|
||||||
|
const init = shouldCache ? `${last} = ${value}` : value;
|
||||||
|
|
||||||
|
const activeKeys = block.getUniqueName(`${node.var}_spread_keys`);
|
||||||
|
block.addVariable(activeKeys, '{}');
|
||||||
|
|
||||||
|
const changes = block.getUniqueName(`${node.var}_spread_changes`);
|
||||||
|
|
||||||
|
const hasNamedAttributes = node.attributes.length;
|
||||||
|
const namedAttributes = block.getUniqueName(`${node.var}_attributes`);
|
||||||
|
|
||||||
|
if (hasNamedAttributes) {
|
||||||
|
block.builders.init.addBlock(deindent`
|
||||||
|
var ${namedAttributes} = [${node.attributes.map(attr => `'${attr.name}'`).join(', ')}];
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dependencies.length || hasChangeableIndex) {
|
||||||
|
const changedCheck = (
|
||||||
|
( block.hasOutroMethod ? `#outroing || ` : '' ) +
|
||||||
|
dependencies.map(dependency => `changed.${dependency}`).join(' || ')
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateCachedValue = `${last} !== (${last} = ${value})`;
|
||||||
|
|
||||||
|
const condition = shouldCache ?
|
||||||
|
( dependencies.length ? `(${changedCheck}) && ${updateCachedValue}` : updateCachedValue ) :
|
||||||
|
changedCheck;
|
||||||
|
|
||||||
|
const oldKeys = block.getUniqueName(`${node.var}_spread_keys_old`);
|
||||||
|
|
||||||
|
updates.push(deindent`
|
||||||
|
if (${condition}) {
|
||||||
|
var ${oldKeys} = ${activeKeys};
|
||||||
|
${activeKeys} = {};
|
||||||
|
|
||||||
|
var ${changes} = ${shouldCache ? last : value};
|
||||||
|
for (var key in ${changes}) {
|
||||||
|
${hasNamedAttributes ? `if (${namedAttributes}.indexOf(key) !== -1) continue;` : ''}
|
||||||
|
|
||||||
|
${node.var}_changes[key] = ${changes}[key];
|
||||||
|
|
||||||
|
${activeKeys}[key] = true;
|
||||||
|
delete ${oldKeys}[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var key in ${oldKeys}) {
|
||||||
|
${node.var}_changes[key] = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<div {{...props}}></div>
|
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"hash": "phg0l6",
|
||||||
|
"html": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 24,
|
||||||
|
"type": "Fragment",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"start": 0,
|
||||||
|
"end": 24,
|
||||||
|
"type": "Element",
|
||||||
|
"name": "div",
|
||||||
|
"attributes": [],
|
||||||
|
"children": [],
|
||||||
|
"spread": {
|
||||||
|
"start": 5,
|
||||||
|
"end": 17,
|
||||||
|
"type": "Spread",
|
||||||
|
"expression": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 10,
|
||||||
|
"end": 15,
|
||||||
|
"name": "props"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"css": null,
|
||||||
|
"js": null
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
<p>foo: {{foo}}</p>
|
||||||
|
<p>baz: {{baz}} ({{typeof baz}})</p>
|
||||||
|
<p>qux: {{qux}}</p>
|
||||||
|
<p>quux: {{quux}}</p>
|
@ -0,0 +1,27 @@
|
|||||||
|
export default {
|
||||||
|
solo: true,
|
||||||
|
|
||||||
|
data: {
|
||||||
|
props: {
|
||||||
|
foo: 'lol',
|
||||||
|
baz: 40 + 2,
|
||||||
|
qux: `this is a ${'piece of'} string`,
|
||||||
|
quux: 'core'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
html: `<div><p>foo: lol</p>\n<p>baz: 42 (number)</p>\n<p>qux: named</p>\n<p>quux: core</p></div>`,
|
||||||
|
|
||||||
|
test ( assert, component, target ) {
|
||||||
|
component.set({
|
||||||
|
props: {
|
||||||
|
foo: 'wut',
|
||||||
|
baz: 40 + 3,
|
||||||
|
qux: `this is a ${'rather boring'} string`,
|
||||||
|
quux: 'heart'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal( target.innerHTML, `<div><p>foo: wut</p>\n<p>baz: 43 (number)</p>\n<p>qux: named</p>\n<p>quux: heart</p></div>` );
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,11 @@
|
|||||||
|
<div>
|
||||||
|
<Widget {{...props}} qux="named"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Widget from './Widget.html';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Widget }
|
||||||
|
};
|
||||||
|
</script>
|
@ -0,0 +1,21 @@
|
|||||||
|
export default {
|
||||||
|
solo: true,
|
||||||
|
|
||||||
|
html: `<div data-named="value" data-foo="bar">red</div>`,
|
||||||
|
|
||||||
|
test ( assert, component, target ) {
|
||||||
|
const div = target.querySelector( 'div' );
|
||||||
|
|
||||||
|
assert.equal( div.dataset.foo, 'bar' );
|
||||||
|
assert.equal( div.dataset.named, 'value' );
|
||||||
|
|
||||||
|
component.set({ color: 'blue', props: { 'data-foo': 'baz', 'data-named': 'qux' } });
|
||||||
|
assert.equal( target.innerHTML, `<div data-named="value" data-foo="baz">blue</div>` );
|
||||||
|
assert.equal( div.dataset.foo, 'baz' );
|
||||||
|
assert.equal( div.dataset.named, 'value' );
|
||||||
|
|
||||||
|
component.set({ color: 'blue', props: {} });
|
||||||
|
assert.equal( target.innerHTML, `<div data-named="value">blue</div>` );
|
||||||
|
assert.equal( div.dataset.foo, undefined );
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,13 @@
|
|||||||
|
<div {{...props}} data-named="value">{{color}}</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data: () => ({
|
||||||
|
color: 'red',
|
||||||
|
props: {
|
||||||
|
'data-foo': 'bar',
|
||||||
|
'data-named': 'qux'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
Reference in new issue