fix contextual references in events

pull/1864/head
Rich Harris 7 years ago
parent 1f0345644f
commit 3da78c7e46

@ -12,8 +12,7 @@ const validBuiltins = new Set(['set', 'fire', 'destroy']);
export default class EventHandler extends Node {
name: string;
modifiers: Set<string>;
dependencies: Set<string>;
expression: Node;
expression: Expression;
callee: any; // TODO
usesComponent: boolean;
@ -31,15 +30,13 @@ export default class EventHandler extends Node {
this.name = info.name;
this.modifiers = new Set(info.modifiers);
this.dependencies = new Set();
if (info.expression) {
this.expression = new Expression(component, parent, template_scope, info.expression, true);
this.snippet = this.expression.snippet;
let { scope, map } = createScopes(info.expression);
walk(this.expression, {
walk(info.expression, {
enter: (node, parent) => {
if (map.has(node)) {
scope = map.get(node);

@ -54,11 +54,12 @@ const precedence: Record<string, (node?: Node) => number> = {
};
export default class Expression {
component: Component;
startnent: Component;
node: any;
snippet: string;
references: Set<string>;
dependencies: Set<string>;
contextual_dependencies: Set<string>;
usesContext = false;
usesEvent = false;
@ -79,6 +80,7 @@ export default class Expression {
this.snippet = `[✂${info.start}-${info.end}✂]`;
const dependencies = new Set();
const contextual_dependencies = new Set();
const { code, helpers } = component;
@ -119,6 +121,7 @@ export default class Expression {
}
expression.usesContext = true;
contextual_dependencies.add(name);
if (!isSynthetic && !isEventHandler) {
// <option> value attribute could be synthetic — avoid double editing
@ -153,6 +156,7 @@ export default class Expression {
});
this.dependencies = dependencies;
this.contextual_dependencies = contextual_dependencies;
}
getPrecedence() {

@ -170,7 +170,7 @@ export default class ElementWrapper extends Wrapper {
});
node.handlers.forEach(handler => {
block.addDependencies(handler.dependencies);
block.addDependencies(handler.expression.dependencies);
});
if (this.parent) {
@ -609,7 +609,6 @@ export default class ElementWrapper extends Wrapper {
const { isCustomEvent } = handler;
// get a name for the event handler that is globally unique
// if hoisted, locally unique otherwise
const handler_name = component.getUniqueName(
`${handler.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_handler`
);
@ -634,15 +633,40 @@ export default class ElementWrapper extends Wrapper {
if (handler.modifiers.has('preventDefault')) modifiers.push('event.preventDefault();');
if (handler.modifiers.has('stopPropagation')) modifiers.push('event.stopPropagation();');
component.event_handlers.push({
name: handler_name,
body: deindent`
function ${handler_name}(event) {
${modifiers}
(${handler.snippet})(event);
}
`
});
let name;
if (handler.expression.contextual_dependencies.size > 0) {
name = handler_name;
const deps = Array.from(handler.expression.contextual_dependencies);
component.event_handlers.push({
name: handler_name,
body: deindent`
function ${handler_name}(event, { ${deps.join(', ')} }) {
(${handler.snippet})(event);
}
`
});
block.builders.init.addBlock(deindent`
${name}.$on("${handler.name}", function(event) {
ctx.${handler_name}.call(this, event, ctx);
});
`);
} else {
name = `ctx.${handler_name}`;
component.event_handlers.push({
name: handler_name,
body: deindent`
function ${handler_name}(event) {
${modifiers}
(${handler.snippet})(event);
}
`
});
}
const opts = ['passive', 'once', 'capture'].filter(mod => handler.modifiers.has(mod));
if (opts.length) {
@ -651,19 +675,19 @@ export default class ElementWrapper extends Wrapper {
: `{ ${opts.map(opt => `${opt}: true`).join(', ')} }`;
block.builders.hydrate.addLine(
`@addListener(${this.var}, "${handler.name}", ctx.${handler_name}, ${optString});`
`@addListener(${this.var}, "${handler.name}", ${name}, ${optString});`
);
block.builders.destroy.addLine(
`@removeListener(${this.var}, "${handler.name}", ctx.${handler_name}, ${optString});`
`@removeListener(${this.var}, "${handler.name}", ${name}, ${optString});`
);
} else {
block.builders.hydrate.addLine(
`@addListener(${this.var}, "${handler.name}", ctx.${handler_name});`
`@addListener(${this.var}, "${handler.name}", ${name});`
);
block.builders.destroy.addLine(
`@removeListener(${this.var}, "${handler.name}", ctx.${handler_name});`
`@removeListener(${this.var}, "${handler.name}", ${name});`
);
}
}

@ -54,7 +54,7 @@ export default class InlineComponentWrapper extends Wrapper {
});
this.node.handlers.forEach(handler => {
block.addDependencies(handler.dependencies);
block.addDependencies(handler.expression.dependencies);
});
this.var = (
@ -428,15 +428,49 @@ export default class InlineComponentWrapper extends Wrapper {
${beforecreate}
${this.node.handlers.map(handler => deindent`
${name}.$on("${handler.name}", function(event) {
(${handler.snippet || `() => { throw new Error('TODO shorthand events'); }`})(event);
});
`)}
${this.node.ref && `#component.$$refs.${this.node.ref.name} = ${name};`}
`);
this.node.handlers.forEach(handler => {
let { snippet } = handler;
if (!snippet) snippet = `() => { throw new Error('TODO shorthand events'); }`;
// get a name for the event handler that is globally unique
const handler_name = component.getUniqueName(
`${handler.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_handler`
);
if (handler.expression.contextual_dependencies.size > 0) {
const deps = Array.from(handler.expression.contextual_dependencies);
component.event_handlers.push({
name: handler_name,
body: deindent`
function ${handler_name}(event, { ${deps.join(', ')} }) {
(${snippet})(event);
}
`
});
block.builders.init.addBlock(deindent`
${name}.$on("${handler.name}", function(event) {
ctx.${handler_name}.call(this, event, ctx);
});
`);
} else {
component.event_handlers.push({
name: handler_name,
body: deindent`
function ${handler_name}(event) {
(${snippet})(event);
}
`
});
block.builders.init.addLine(`${name}.$on("${handler.name}", ctx.${handler_name});`);
}
});
block.builders.create.addLine(`${name}.$$fragment.c();`);
if (parentNodes) {

@ -45,7 +45,7 @@ export default class WindowWrapper extends Wrapper {
const isCustomEvent = component.events.has(handler.name);
let usesState = handler.dependencies.size > 0;
let usesState = handler.expression.dependencies.size > 0;
const handler_name = block.getUniqueName(`onwindow${handler.name}`);
const handler_body = deindent`

Loading…
Cancel
Save