pull/1367/head
Rich Harris 7 years ago
parent 9ff1beec48
commit 7825c1230a

@ -1,7 +1,18 @@
import Node from './shared/Node'; import Node from './shared/Node';
import Expression from './shared/Expression';
export default class Action extends Node { export default class Action extends Node {
type: 'Action';
name: string; name: string;
value: Node[] expression: Expression;
expression: Node
constructor(compiler, parent, info) {
super(compiler, parent, info);
this.name = info.name;
this.expression = info.expression
? new Expression(compiler, this, info.expression)
: null;
}
} }

@ -47,7 +47,34 @@ export default class Attribute extends Node {
return expression; return expression;
}); });
this.isDynamic = this.dependencies.size > 0; this.isDynamic = this.chunks.length === 1
? this.chunks[0].type !== 'Text'
: this.chunks.length > 1;
// TODO this would be better, but it breaks some stuff
// this.isDynamic = this.dependencies.size > 0;
}
getValue() {
if (this.isTrue) return true;
if (this.chunks.length === 0) return `''`;
if (this.chunks.length === 1) {
return this.chunks[0].type === 'Text'
? stringify(this.chunks[0].data)
: this.chunks[0].snippet;
}
return (this.chunks[0].type === 'Text' ? '' : `"" + `) +
this.chunks
.map((chunk: Node) => {
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
return getExpressionPrecedence(chunk) <= 13 ? `(${chunk.snippet})` : snippet;
}
})
.join(' + ');
} }
render(block: Block) { render(block: Block) {
@ -287,7 +314,7 @@ export default class Attribute extends Node {
allDependencies.add(d); allDependencies.add(d);
}); });
return getExpressionPrecedence(chunk.expression) <= 13 ? `( ${snippet} )` : snippet; return getExpressionPrecedence(chunk) <= 13 ? `(${snippet})` : snippet;
} }
}) })
.join(' + '); .join(' + ');

@ -17,12 +17,36 @@ const readOnlyMediaAttributes = new Set([
export default class Binding extends Node { export default class Binding extends Node {
name: string; name: string;
value: Expression; value: Expression;
obj: string;
prop: string;
constructor(compiler, parent, info) { constructor(compiler, parent, info) {
super(compiler, parent, info); super(compiler, parent, info);
this.name = info.name; this.name = info.name;
this.value = new Expression(compiler, this, info.value); this.value = new Expression(compiler, this, info.value);
// const contextual = block.contexts.has(name);
const contextual = false; // TODO
let obj;
let prop;
if (contextual) {
// TODO does this need to go later?
obj = `ctx.${block.listNames.get(name)}`;
prop = `${block.indexNames.get(name)}`;
} else if (this.value.node.type === 'MemberExpression') {
prop = `[✂${this.value.node.property.start}-${this.value.node.property.end}✂]`;
if (!this.value.node.computed) prop = `'${prop}'`;
obj = `[✂${this.value.node.object.start}-${this.value.node.object.end}✂]`;
} else {
obj = 'ctx';
prop = `'${this.name}'`;
}
this.obj = obj;
this.prop = prop;
} }
munge( munge(

@ -12,12 +12,17 @@ import Block from '../dom/Block';
import Attribute from './Attribute'; import Attribute from './Attribute';
import usesThisOrArguments from '../../validate/js/utils/usesThisOrArguments'; import usesThisOrArguments from '../../validate/js/utils/usesThisOrArguments';
import mapChildren from './shared/mapChildren'; import mapChildren from './shared/mapChildren';
import Binding from './Binding';
import EventHandler from './EventHandler';
export default class Component extends Node { export default class Component extends Node {
type: 'Component'; type: 'Component';
name: string; name: string;
attributes: Attribute[]; attributes: Attribute[];
bindings: Binding[];
handlers: EventHandler[];
children: Node[]; children: Node[];
ref: string;
constructor(compiler, parent, info) { constructor(compiler, parent, info) {
super(compiler, parent, info); super(compiler, parent, info);
@ -27,7 +32,8 @@ export default class Component extends Node {
this.name = info.name; this.name = info.name;
this.attributes = []; this.attributes = [];
// TODO bindings etc this.bindings = [];
this.handlers = [];
info.attributes.forEach(node => { info.attributes.forEach(node => {
switch (node.type) { switch (node.type) {
@ -36,6 +42,22 @@ export default class Component extends Node {
this.attributes.push(new Attribute(compiler, this, node)); this.attributes.push(new Attribute(compiler, this, node));
break; break;
case 'Binding':
this.bindings.push(new Binding(compiler, this, node));
break;
case 'EventHandler':
this.handlers.push(new EventHandler(compiler, this, node));
break;
case 'Ref':
// TODO catch this in validation
if (this.ref) throw new Error(`Duplicate refs`);
compiler.usesRefs = true
this.ref = node.name;
break;
default: default:
throw new Error(`Not implemented: ${node.type}`); throw new Error(`Not implemented: ${node.type}`);
} }
@ -51,23 +73,16 @@ export default class Component extends Node {
) { ) {
this.cannotUseInnerHTML(); this.cannotUseInnerHTML();
this.attributes.forEach((attribute: Node) => { this.attributes.forEach(attr => {
if (attribute.type === 'Attribute' && attribute.value !== true) { block.addDependencies(attr.dependencies);
attribute.value.forEach((chunk: Node) => {
if (chunk.type !== 'Text') {
const dependencies = chunk.metadata.dependencies;
block.addDependencies(dependencies);
}
}); });
} else {
if (attribute.type === 'EventHandler' && attribute.expression) { this.bindings.forEach(binding => {
attribute.expression.arguments.forEach((arg: Node) => { block.addDependencies(binding.value.dependencies);
block.addDependencies(arg.metadata.dependencies);
}); });
} else if (attribute.type === 'Binding' || attribute.type === 'Spread') {
block.addDependencies(attribute.metadata.dependencies); this.handlers.forEach(handler => {
} block.addDependencies(handler.dependencies);
}
}); });
this.var = block.getUniqueName( this.var = block.getUniqueName(
@ -115,60 +130,61 @@ export default class Component extends Node {
let name_updating: string; let name_updating: string;
let beforecreate: string = null; let beforecreate: string = null;
const attributes = this.attributes // const attributes = this.attributes
.filter(a => a.type === 'Attribute' || a.type === 'Spread') // .filter(a => a.type === 'Attribute' || a.type === 'Spread')
.map(a => mungeAttribute(a, block)); // .map(a => mungeAttribute(a, block));
const bindings = this.attributes
.filter(a => a.type === 'Binding')
.map(a => mungeBinding(a, block));
const eventHandlers = this.attributes // const bindings = this.attributes
.filter((a: Node) => a.type === 'EventHandler') // .filter(a => a.type === 'Binding')
.map(a => mungeEventHandler(compiler, this, a, block, allContexts)); // .map(a => mungeBinding(a, block));
const ref = this.attributes.find((a: Node) => a.type === 'Ref'); // const eventHandlers = this.attributes
if (ref) compiler.usesRefs = true; // .filter((a: Node) => a.type === 'EventHandler')
// .map(a => mungeEventHandler(compiler, this, a, block, allContexts));
const updates: string[] = []; const updates: string[] = [];
const usesSpread = !!attributes.find(a => a.spread); const usesSpread = !!this.attributes.find(a => a.spread);
const attributeObject = usesSpread const attributeObject = usesSpread
? '{}' ? '{}'
: stringifyProps( : stringifyProps(
attributes.map((attribute: Attribute) => `${attribute.name}: ${attribute.value}`) // this.attributes.map(attr => `${attr.name}: ${attr.value}`)
this.attributes.map(attr => `${attr.name}: "TODO"`)
); );
if (attributes.length || bindings.length) { if (this.attributes.length || this.bindings.length) {
componentInitProperties.push(`data: ${name_initial_data}`); componentInitProperties.push(`data: ${name_initial_data}`);
} }
if ((!usesSpread && attributes.filter(a => a.dynamic).length) || bindings.length) { if ((!usesSpread && this.attributes.filter(a => a.isDynamic).length) || this.bindings.length) {
updates.push(`var ${name_changes} = {};`); updates.push(`var ${name_changes} = {};`);
} }
if (attributes.length) { if (this.attributes.length) {
if (usesSpread) { if (usesSpread) {
const levels = block.getUniqueName(`${this.var}_spread_levels`); const levels = block.getUniqueName(`${this.var}_spread_levels`);
const initialProps = []; const initialProps = [];
const changes = []; const changes = [];
attributes this.attributes
.forEach(munged => { .forEach(attr => {
const { spread, name, dynamic, value, dependencies } = munged; const { spread, name, dependencies } = attr;
const value = attr.getValue();
const condition = dependencies.size > 0
? [...dependencies].map(d => `changed.${d}`).join(' || ')
: null;
if (spread) { if (spread) {
initialProps.push(value); initialProps.push(value);
const condition = dependencies && dependencies.map(d => `changed.${d}`).join(' || ');
changes.push(condition ? `${condition} && ${value}` : value); changes.push(condition ? `${condition} && ${value}` : value);
} else { } else {
const obj = `{ ${quoteIfNecessary(name)}: ${value} }`; const obj = `{ ${quoteIfNecessary(name)}: ${value} }`;
initialProps.push(obj); initialProps.push(obj);
const condition = dependencies && dependencies.map(d => `changed.${d}`).join(' || ');
changes.push(condition ? `${condition} && ${obj}` : obj); changes.push(condition ? `${condition} && ${obj}` : obj);
} }
}); });
@ -191,27 +207,27 @@ export default class Component extends Node {
]); ]);
`); `);
} else { } else {
attributes this.attributes
.filter((attribute: Attribute) => attribute.dynamic) .filter((attribute: Attribute) => attribute.isDynamic)
.forEach((attribute: Attribute) => { .forEach((attribute: Attribute) => {
if (attribute.dependencies.length) { if (attribute.dependencies.size > 0) {
updates.push(deindent` updates.push(deindent`
if (${attribute.dependencies if (${[...attribute.dependencies]
.map(dependency => `changed.${dependency}`) .map(dependency => `changed.${dependency}`)
.join(' || ')}) ${name_changes}.${attribute.name} = ${attribute.value}; .join(' || ')}) ${name_changes}.${attribute.name} = ${attribute.getValue()};
`); `);
} }
else { else {
// TODO this is an odd situation to encounter I *think* it should only happen with // TODO this is an odd situation to encounter I *think* it should only happen with
// each block indices, in which case it may be possible to optimise this // each block indices, in which case it may be possible to optimise this
updates.push(`${name_changes}.${attribute.name} = ${attribute.value};`); updates.push(`${name_changes}.${attribute.name} = ${attribute.getValue()};`);
} }
}); });
} }
} }
if (bindings.length) { if (this.bindings.length) {
compiler.hasComplexBindings = true; compiler.hasComplexBindings = true;
name_updating = block.alias(`${name}_updating`); name_updating = block.alias(`${name}_updating`);
@ -222,18 +238,18 @@ export default class Component extends Node {
const builder = new CodeBuilder(); const builder = new CodeBuilder();
bindings.forEach((binding: Binding) => { this.bindings.forEach((binding: Binding) => {
let { name: key } = getObject(binding.value); let { name: key } = getObject(binding.value.node);
binding.contexts.forEach(context => { binding.value.contexts.forEach(context => {
allContexts.add(context); allContexts.add(context);
}); });
let setFromChild; let setFromChild;
if (block.contexts.has(key)) { if (block.contexts.has(key)) {
const computed = isComputed(binding.value); const computed = isComputed(binding.value.node);
const tail = binding.value.type === 'MemberExpression' ? getTailSnippet(binding.value) : ''; const tail = binding.value.node.type === 'MemberExpression' ? getTailSnippet(binding.value.node) : '';
const list = block.listNames.get(key); const list = block.listNames.get(key);
const index = block.indexNames.get(key); const index = block.indexNames.get(key);
@ -241,7 +257,7 @@ export default class Component extends Node {
setFromChild = deindent` setFromChild = deindent`
${list}[${index}]${tail} = childState.${binding.name}; ${list}[${index}]${tail} = childState.${binding.name};
${binding.dependencies ${[...binding.value.dependencies]
.map((name: string) => { .map((name: string) => {
const isStoreProp = name[0] === '$'; const isStoreProp = name[0] === '$';
const prop = isStoreProp ? name.slice(1) : name; const prop = isStoreProp ? name.slice(1) : name;
@ -263,9 +279,9 @@ export default class Component extends Node {
if (isStoreProp) hasStoreBindings = true; if (isStoreProp) hasStoreBindings = true;
else hasLocalBindings = true; else hasLocalBindings = true;
if (binding.value.type === 'MemberExpression') { if (binding.value.node.type === 'MemberExpression') {
setFromChild = deindent` setFromChild = deindent`
${binding.snippet} = childState.${binding.name}; ${binding.value.snippet} = childState.${binding.name};
${newState}.${prop} = state.${key}; ${newState}.${prop} = state.${key};
`; `;
} }
@ -277,7 +293,7 @@ export default class Component extends Node {
statements.push(deindent` statements.push(deindent`
if (${binding.prop} in ${binding.obj}) { if (${binding.prop} in ${binding.obj}) {
${name_initial_data}.${binding.name} = ${binding.snippet}; ${name_initial_data}.${binding.name} = ${binding.value.snippet};
${name_updating}.${binding.name} = true; ${name_updating}.${binding.name} = true;
}` }`
); );
@ -288,8 +304,8 @@ export default class Component extends Node {
); );
updates.push(deindent` updates.push(deindent`
if (!${name_updating}.${binding.name} && ${binding.dependencies.map((dependency: string) => `changed.${dependency}`).join(' || ')}) { if (!${name_updating}.${binding.name} && ${[...binding.value.dependencies].map((dependency: string) => `changed.${dependency}`).join(' || ')}) {
${name_changes}.${binding.name} = ${binding.snippet}; ${name_changes}.${binding.name} = ${binding.value.snippet};
${name_updating}.${binding.name} = true; ${name_updating}.${binding.name} = true;
} }
`); `);
@ -314,7 +330,7 @@ export default class Component extends Node {
beforecreate = deindent` beforecreate = deindent`
#component.root._beforecreate.push(function() { #component.root._beforecreate.push(function() {
${name}._bind({ ${bindings.map(b => `${b.name}: 1`).join(', ')} }, ${name}.get()); ${name}._bind({ ${this.bindings.map(b => `${b.name}: 1`).join(', ')} }, ${name}.get());
}); });
`; `;
} }
@ -323,8 +339,7 @@ export default class Component extends Node {
const switch_value = block.getUniqueName('switch_value'); const switch_value = block.getUniqueName('switch_value');
const switch_props = block.getUniqueName('switch_props'); const switch_props = block.getUniqueName('switch_props');
block.contextualise(this.expression); const { dependencies, snippet } = this.expression;
const { dependencies, snippet } = this.metadata;
const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes); const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
@ -332,7 +347,7 @@ export default class Component extends Node {
var ${switch_value} = ${snippet}; var ${switch_value} = ${snippet};
function ${switch_props}(state) { function ${switch_props}(state) {
${(attributes.length || bindings.length) && deindent` ${(this.attributes.length || this.bindings.length) && deindent`
var ${name_initial_data} = ${attributeObject};`} var ${name_initial_data} = ${attributeObject};`}
${statements} ${statements}
return { return {
@ -346,7 +361,7 @@ export default class Component extends Node {
${beforecreate} ${beforecreate}
} }
${eventHandlers.map(handler => deindent` ${this.handlers.map(handler => deindent`
function ${handler.var}(event) { function ${handler.var}(event) {
${handler.body} ${handler.body}
} }
@ -368,7 +383,7 @@ export default class Component extends Node {
block.builders.mount.addBlock(deindent` block.builders.mount.addBlock(deindent`
if (${name}) { if (${name}) {
${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'}); ${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});
${ref && `#component.refs.${ref.name} = ${name};`} ${this.ref && `#component.refs.${this.ref} = ${name};`}
} }
`); `);
@ -389,12 +404,12 @@ export default class Component extends Node {
${name}.on("${handler.name}", ${handler.var}); ${name}.on("${handler.name}", ${handler.var});
`)} `)}
${ref && `#component.refs.${ref.name} = ${name};`} ${this.ref && `#component.refs.${this.ref} = ${name};`}
} }
${ref && deindent` ${this.ref && deindent`
else if (#component.refs.${ref.name} === ${name}) { else if (#component.refs.${this.ref} === ${name}) {
#component.refs.${ref.name} = null; #component.refs.${this.ref} = null;
}`} }`}
} }
`); `);
@ -418,7 +433,7 @@ export default class Component extends Node {
: `%components-${this.name}`; : `%components-${this.name}`;
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
${(attributes.length || bindings.length) && deindent` ${(this.attributes.length || this.bindings.length) && deindent`
var ${name_initial_data} = ${attributeObject};`} var ${name_initial_data} = ${attributeObject};`}
${statements} ${statements}
var ${name} = new ${expression}({ var ${name} = new ${expression}({
@ -427,13 +442,13 @@ export default class Component extends Node {
${beforecreate} ${beforecreate}
${eventHandlers.map(handler => deindent` ${this.handlers.map(handler => deindent`
${name}.on("${handler.name}", function(event) { ${name}.on("${handler.name}", function(event) {
${handler.body} ${handler.body}
}); });
`)} `)}
${ref && `#component.refs.${ref.name} = ${name};`} ${this.ref && `#component.refs.${this.ref} = ${name};`}
`); `);
block.builders.create.addLine(`${name}._fragment.c();`); block.builders.create.addLine(`${name}._fragment.c();`);
@ -452,7 +467,7 @@ export default class Component extends Node {
block.builders.update.addBlock(deindent` block.builders.update.addBlock(deindent`
${updates} ${updates}
${name}._set(${name_changes}); ${name}._set(${name_changes});
${bindings.length && `${name_updating} = {};`} ${this.bindings.length && `${name_updating} = {};`}
`); `);
} }
@ -460,7 +475,7 @@ export default class Component extends Node {
block.builders.destroy.addLine(deindent` block.builders.destroy.addLine(deindent`
${name}.destroy(false); ${name}.destroy(false);
${ref && `if (#component.refs.${ref.name} === ${name}) #component.refs.${ref.name} = null;`} ${this.ref && `if (#component.refs.${this.ref} === ${name}) #component.refs.${this.ref} = null;`}
`); `);
} }
} }
@ -472,8 +487,7 @@ export default class Component extends Node {
function mungeBinding(binding: Node, block: Block): Binding { function mungeBinding(binding: Node, block: Block): Binding {
const { name } = getObject(binding.value); const { name } = getObject(binding.value);
const { contexts } = block.contextualise(binding.value); const { dependencies, snippet } = binding.expression;
const { dependencies, snippet } = binding.metadata;
const contextual = block.contexts.has(name); const contextual = block.contexts.has(name);

@ -23,6 +23,7 @@ export default class Element extends Node {
type: 'Element'; type: 'Element';
name: string; name: string;
attributes: Attribute[]; attributes: Attribute[];
actions: Action[];
bindings: Binding[]; bindings: Binding[];
handlers: EventHandler[]; handlers: EventHandler[];
intro: Transition; intro: Transition;
@ -42,6 +43,7 @@ export default class Element extends Node {
parentElement ? parentElement.namespace : this.compiler.namespace; parentElement ? parentElement.namespace : this.compiler.namespace;
this.attributes = []; this.attributes = [];
this.actions = [];
this.bindings = []; this.bindings = [];
this.handlers = []; this.handlers = [];
@ -50,6 +52,10 @@ export default class Element extends Node {
info.attributes.forEach(node => { info.attributes.forEach(node => {
switch (node.type) { switch (node.type) {
case 'Action':
this.actions.push(new Action(compiler, this, node));
break;
case 'Attribute': case 'Attribute':
// special case // special case
if (node.name === 'xmlns') this.namespace = node.value[0].data; if (node.name === 'xmlns') this.namespace = node.value[0].data;
@ -119,6 +125,13 @@ export default class Element extends Node {
} }
}); });
this.actions.forEach(action => {
this.cannotUseInnerHTML();
if (action.expression) {
block.addDependencies(action.expression.dependencies);
}
});
this.bindings.forEach(binding => { this.bindings.forEach(binding => {
this.cannotUseInnerHTML(); this.cannotUseInnerHTML();
block.addDependencies(binding.value.dependencies); block.addDependencies(binding.value.dependencies);
@ -144,9 +157,7 @@ export default class Element extends Node {
} else { } else {
if (this.parent) this.parent.cannotUseInnerHTML(); if (this.parent) this.parent.cannotUseInnerHTML();
if (attribute.type === 'Action' && attribute.expression) { if (attribute.type === 'Spread') {
block.addDependencies(attribute.metadata.dependencies);
} else if (attribute.type === 'Spread') {
block.addDependencies(attribute.metadata.dependencies); block.addDependencies(attribute.metadata.dependencies);
} }
} }
@ -739,31 +750,29 @@ export default class Element extends Node {
} }
addActions(block: Block) { addActions(block: Block) {
this.attributes.filter((a: Action) => a.type === 'Action').forEach((attribute:Action) => { this.actions.forEach(action => {
const { expression } = attribute; const { expression } = action;
let snippet, dependencies; let snippet, dependencies;
if (expression) { if (expression) {
this.compiler.addSourcemapLocations(expression); snippet = action.expression.snippet;
block.contextualise(expression); dependencies = action.expression.dependencies;
snippet = attribute.metadata.snippet;
dependencies = attribute.metadata.dependencies;
} }
const name = block.getUniqueName( const name = block.getUniqueName(
`${attribute.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_action` `${action.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_action`
); );
block.addVariable(name); block.addVariable(name);
const fn = `%actions-${attribute.name}`; const fn = `%actions-${action.name}`;
block.builders.hydrate.addLine( block.builders.hydrate.addLine(
`${name} = ${fn}.call(#component, ${this.var}${snippet ? `, ${snippet}` : ''}) || {};` `${name} = ${fn}.call(#component, ${this.var}${snippet ? `, ${snippet}` : ''}) || {};`
); );
if (dependencies && dependencies.length) { if (dependencies && dependencies.size > 0) {
let conditional = `typeof ${name}.update === 'function' && `; let conditional = `typeof ${name}.update === 'function' && `;
const deps = dependencies.map(dependency => `changed.${dependency}`).join(' || '); const deps = [...dependencies].map(dependency => `changed.${dependency}`).join(' || ');
conditional += dependencies.length > 1 ? `(${deps})` : deps; conditional += dependencies.size > 1 ? `(${deps})` : deps;
block.builders.update.addConditional( block.builders.update.addConditional(
conditional, conditional,

@ -71,14 +71,7 @@ export default class Window extends Node {
let usesState = handler.dependencies.size > 0; let usesState = handler.dependencies.size > 0;
// const flattened = flattenReference(handler.expression.callee); handler.render(compiler, block);
// if (flattened.name !== 'event' && flattened.name !== 'this') {
// // allow event.stopPropagation(), this.select() etc
// compiler.code.prependRight(
// handler.expression.start,
// `${block.alias('component')}.`
// );
// }
const handlerName = block.getUniqueName(`onwindow${handler.name}`); const handlerName = block.getUniqueName(`onwindow${handler.name}`);
const handlerBody = deindent` const handlerBody = deindent`

@ -11,6 +11,8 @@ export default class Expression {
references: Set<string>; references: Set<string>;
dependencies: Set<string>; dependencies: Set<string>;
contexts: Set<string>;
indexes: Set<string>;
constructor(compiler, parent, info) { constructor(compiler, parent, info) {
this.compiler = compiler; this.compiler = compiler;
@ -26,6 +28,7 @@ export default class Expression {
const { code, helpers } = compiler; const { code, helpers } = compiler;
let { map, scope } = createScopes(info); let { map, scope } = createScopes(info);
const isEventHandler = parent.type === 'EventHandler';
walk(info, { walk(info, {
enter(node: any, parent: any) { enter(node: any, parent: any) {
@ -38,11 +41,11 @@ export default class Expression {
} }
if (isReference(node, parent)) { if (isReference(node, parent)) {
code.prependRight(node.start, 'ctx.');
const { name } = flattenReference(node); const { name } = flattenReference(node);
if (scope && scope.has(name) || helpers.has(name) || (name === 'event' && isEventHandler)) return; if (scope && scope.has(name) || helpers.has(name) || (name === 'event' && isEventHandler)) return;
code.prependRight(node.start, 'ctx.');
if (contextDependencies.has(name)) { if (contextDependencies.has(name)) {
contextDependencies.get(name).forEach(dependency => { contextDependencies.get(name).forEach(dependency => {
dependencies.add(dependency); dependencies.add(dependency);

@ -68,18 +68,17 @@ export default function visitElement(
if (attribute.name === 'value' && node.name === 'textarea') { if (attribute.name === 'value' && node.name === 'textarea') {
textareaContents = stringifyAttributeValue(block, attribute.value); textareaContents = stringifyAttributeValue(block, attribute.value);
} else if (attribute.value === true) { } else if (attribute.isTrue) {
openingTag += ` ${attribute.name}`; openingTag += ` ${attribute.name}`;
} else if ( } else if (
booleanAttributes.has(attribute.name) && booleanAttributes.has(attribute.name) &&
attribute.value.length === 1 && attribute.chunks.length === 1 &&
attribute.value[0].type !== 'Text' attribute.chunks[0].type !== 'Text'
) { ) {
// a boolean attribute with one non-Text chunk // a boolean attribute with one non-Text chunk
block.contextualise(attribute.value[0].expression); openingTag += '${' + attribute.chunks[0].snippet + ' ? " ' + attribute.name + '" : "" }';
openingTag += '${' + attribute.value[0].metadata.snippet + ' ? " ' + attribute.name + '" : "" }';
} else { } else {
openingTag += ` ${attribute.name}="${stringifyAttributeValue(block, attribute.value)}"`; openingTag += ` ${attribute.name}="${stringifyAttributeValue(block, attribute.chunks)}"`;
} }
}); });
} }

@ -9,9 +9,7 @@ export default function stringifyAttributeValue(block: Block, chunks: Node[]) {
return escapeTemplate(escape(chunk.data).replace(/"/g, '&quot;')); return escapeTemplate(escape(chunk.data).replace(/"/g, '&quot;'));
} }
block.contextualise(chunk.expression); return '${__escape(' + chunk.snippet + ')}';
const { snippet } = chunk.metadata;
return '${__escape(' + snippet + ')}';
}) })
.join(''); .join('');
} }

@ -29,12 +29,19 @@ export default function validateHtml(validator: Validator, html: Node) {
validateHead(validator, node, refs, refCallees); validateHead(validator, node, refs, refCallees);
} }
else if (node.type === 'Element') { else if (node.type === 'Component' || node.name === 'svelte:self' || node.name === 'svelte:component') {
const isComponent = validateElement(
node.name === 'svelte:self' || validator,
node.name === 'svelte:component' || node,
validator.components.has(node.name); refs,
refCallees,
stack,
elementStack,
true
);
}
else if (node.type === 'Element') {
validateElement( validateElement(
validator, validator,
node, node,
@ -42,13 +49,11 @@ export default function validateHtml(validator: Validator, html: Node) {
refCallees, refCallees,
stack, stack,
elementStack, elementStack,
isComponent false
); );
if (!isComponent) {
a11y(validator, node, elementStack); a11y(validator, node, elementStack);
} }
}
else if (node.type === 'EachBlock') { else if (node.type === 'EachBlock') {
if (validator.helpers.has(node.context)) { if (validator.helpers.has(node.context)) {

Loading…
Cancel
Save