Lint and format src/compiler/compile/nodes/shared/Expression.js

pull/8569/head
Simon Holthausen 3 years ago
parent b0f9d079c3
commit 160cdc948b

@ -14,467 +14,457 @@ import { clone } from '../../../utils/clone.js';
import compiler_errors from '../../compiler_errors.js'; import compiler_errors from '../../compiler_errors.js';
const regex_contains_term_function_expression = /FunctionExpression/; const regex_contains_term_function_expression = /FunctionExpression/;
export default class Expression { export default class Expression {
/** @type {'Expression'} */
/** @type {'Expression'} */ type = 'Expression';
type = ('Expression');
/** @type {import('../../Component.js').default} */
/** @type {import('../../Component.js').default} */ component;
component;
/** @type {Owner} */
/** @type {Owner} */ owner;
owner;
/** @type {import('estree').Node} */
/** @type {import('estree').Node} */ node;
node;
/** @type {Set<string>} */
/** @type {Set<string>} */ references = new Set();
references = new Set();
/** @type {Set<string>} */
/** @type {Set<string>} */ dependencies = new Set();
dependencies = new Set();
/** @type {Set<string>} */
/** @type {Set<string>} */ contextual_dependencies = new Set();
contextual_dependencies = new Set();
/** @type {import('./TemplateScope.js').default} */
/** @type {import('./TemplateScope.js').default} */ template_scope;
template_scope;
/** @type {import('../../utils/scope.js').Scope} */
/** @type {import('../../utils/scope.js').Scope} */ scope;
scope;
/** @type {WeakMap<import('estree').Node, import('../../utils/scope.js').Scope>} */
/** @type {WeakMap<import('estree').Node, import('../../utils/scope.js').Scope>} */ scope_map;
scope_map;
/** @type {Array<import('estree').Node | import('estree').Node[]>} */
/** @type {Array<import('estree').Node | import('estree').Node[]>} */ declarations = [];
declarations = []; /** */
/** */ uses_context = false;
uses_context = false;
/** @type {import('estree').Node} */
/** @type {import('estree').Node} */ manipulated;
manipulated;
/**
/** * @param {import('../../Component.js').default} component *
* @param {import('../../Component.js').default} component * * @param {Owner} owner *
* @param {Owner} owner * * @param {import('./TemplateScope.js').default} template_scope *
* @param {import('./TemplateScope.js').default} template_scope * * @param {import('estree').Node} info *
* @param {import('estree').Node} info * * @param {boolean} [lazy] undefined
* @param {boolean} [lazy] undefined */
*/ constructor(component, owner, template_scope, info, lazy) {
constructor(component, owner, template_scope, info, lazy) { // TODO revert to direct property access in prod?
// TODO revert to direct property access in prod? Object.defineProperties(this, {
Object.defineProperties(this, { component: {
component: { value: component
value: component }
} });
}); this.node = info;
this.node = info; this.template_scope = template_scope;
this.template_scope = template_scope; this.owner = owner;
this.owner = owner; const { dependencies, contextual_dependencies, references } = this;
const { dependencies, contextual_dependencies, references } = this; let { map, scope } = create_scopes(info);
let { map, scope } = create_scopes(info); this.scope = scope;
this.scope = scope; this.scope_map = map;
this.scope_map = map; const expression = this;
const expression = this; let function_expression;
let function_expression; // discover dependencies, but don't change the code yet
// discover dependencies, but don't change the code yet walk(info, {
walk(info, { /**
* @param {any} node
/** * @param {any} parent
* @param {any} node * @param {string} key
* @param {any} parent */
* @param {string} key enter(node, parent, key) {
*/ // don't manipulate shorthand props twice
enter(node, parent, key) { if (key === 'key' && parent.shorthand) return;
// don't manipulate shorthand props twice // don't manipulate `import.meta`, `new.target`
if (key === 'key' && parent.shorthand) if (node.type === 'MetaProperty') return this.skip();
return; if (map.has(node)) {
// don't manipulate `import.meta`, `new.target` scope = map.get(node);
if (node.type === 'MetaProperty') }
return this.skip(); if (!function_expression && regex_contains_term_function_expression.test(node.type)) {
if (map.has(node)) { function_expression = node;
scope = map.get(node); }
} if (is_reference(node, parent)) {
if (!function_expression && regex_contains_term_function_expression.test(node.type)) { const { name, nodes } = flatten_reference(node);
function_expression = node; references.add(name);
} if (scope.has(name)) return;
if (is_reference(node, parent)) { if (name[0] === '$') {
const { name, nodes } = flatten_reference(node); const store_name = name.slice(1);
references.add(name); if (template_scope.names.has(store_name) || scope.has(store_name)) {
if (scope.has(name)) return component.error(node, compiler_errors.contextual_store);
return; }
if (name[0] === '$') { }
const store_name = name.slice(1); if (template_scope.is_let(name)) {
if (template_scope.names.has(store_name) || scope.has(store_name)) { if (!lazy) {
return component.error(node, compiler_errors.contextual_store); contextual_dependencies.add(name);
} dependencies.add(name);
} }
if (template_scope.is_let(name)) { } else if (template_scope.names.has(name)) {
if (!lazy) { expression.uses_context = true;
contextual_dependencies.add(name); contextual_dependencies.add(name);
dependencies.add(name); const owner = template_scope.get_owner(name);
} const is_index = owner.type === 'EachBlock' && owner.key && name === owner.index;
} if (!lazy || is_index) {
else if (template_scope.names.has(name)) { template_scope.dependencies_for_name
expression.uses_context = true; .get(name)
contextual_dependencies.add(name); .forEach(/** @param {any} name */ (name) => dependencies.add(name));
const owner = template_scope.get_owner(name); }
const is_index = owner.type === 'EachBlock' && owner.key && name === owner.index; } else {
if (!lazy || is_index) { if (!lazy) {
template_scope.dependencies_for_name dependencies.add(name);
.get(name) }
.forEach(/** @param {any} name */ (name) => dependencies.add(name)); component.add_reference(node, name);
} component.warn_if_undefined(name, nodes[0], template_scope);
} }
else { this.skip();
if (!lazy) { }
dependencies.add(name); // track any assignments from template expressions as mutable
} let names;
component.add_reference(node, name); let deep = false;
component.warn_if_undefined(name, nodes[0], template_scope); if (function_expression) {
} if (node.type === 'AssignmentExpression') {
this.skip(); deep = node.left.type === 'MemberExpression';
} names = extract_names(deep ? get_object(node.left) : node.left);
// track any assignments from template expressions as mutable } else if (node.type === 'UpdateExpression') {
let names; deep = node.argument.type === 'MemberExpression';
let deep = false; names = extract_names(get_object(node.argument));
if (function_expression) { }
if (node.type === 'AssignmentExpression') { }
deep = node.left.type === 'MemberExpression'; if (names) {
names = extract_names(deep ? get_object(node.left) : node.left); names.forEach(
} /** @param {any} name */ (name) => {
else if (node.type === 'UpdateExpression') { if (template_scope.names.has(name)) {
deep = node.argument.type === 'MemberExpression'; if (template_scope.is_const(name)) {
names = extract_names(get_object(node.argument)); component.error(node, compiler_errors.invalid_const_update(name));
} }
} template_scope.dependencies_for_name.get(name).forEach(
if (names) { /** @param {any} name */ (name) => {
names.forEach(/** @param {any} name */ (name) => { const variable = component.var_lookup.get(name);
if (template_scope.names.has(name)) { if (variable) variable[deep ? 'mutated' : 'reassigned'] = true;
if (template_scope.is_const(name)) { }
component.error(node, compiler_errors.invalid_const_update(name)); );
} const each_block = template_scope.get_owner(name);
template_scope.dependencies_for_name.get(name).forEach(/** @param {any} name */ (name) => { /** @type {import('../EachBlock.js').default} */ (each_block).has_binding = true;
const variable = component.var_lookup.get(name); } else {
if (variable) component.add_reference(node, name);
variable[deep ? 'mutated' : 'reassigned'] = true; const variable = component.var_lookup.get(name);
}); if (variable) {
const each_block = template_scope.get_owner(name); variable[deep ? 'mutated' : 'reassigned'] = true;
( /** @type {import('../EachBlock.js').default} */(each_block)).has_binding = true; }
}
else { /** @type {any} */
component.add_reference(node, name); const declaration = scope.find_owner(name)?.declarations.get(name);
const variable = component.var_lookup.get(name); if (declaration) {
if (variable) { if (declaration.kind === 'const' && !deep) {
variable[deep ? 'mutated' : 'reassigned'] = true; component.error(node, {
} code: 'assignment-to-const',
message: 'You are assigning to a const'
/** @type {any} */ });
const declaration = scope.find_owner(name)?.declarations.get(name); }
if (declaration) { } else if (variable && variable.writable === false && !deep) {
if (declaration.kind === 'const' && !deep) { component.error(node, {
component.error(node, { code: 'assignment-to-const',
code: 'assignment-to-const', message: 'You are assigning to a const'
message: 'You are assigning to a const' });
}); }
} }
} }
else if (variable && variable.writable === false && !deep) { );
component.error(node, { }
code: 'assignment-to-const', },
message: 'You are assigning to a const'
}); /** @param {import('estree').Node} node */
} leave(node) {
} if (map.has(node)) {
}); scope = scope.parent;
} }
}, if (node === function_expression) {
function_expression = null;
/** @param {import('estree').Node} node */ }
leave(node) { }
if (map.has(node)) { });
scope = scope.parent; }
} dynamic_dependencies() {
if (node === function_expression) { return Array.from(this.dependencies).filter(
function_expression = null; /** @param {any} name */ (name) => {
} if (this.template_scope.is_let(name)) return true;
} if (is_reserved_keyword(name)) return true;
}); const variable = this.component.var_lookup.get(name);
} return is_dynamic(variable);
dynamic_dependencies() { }
return Array.from(this.dependencies).filter(/** @param {any} name */ (name) => { );
if (this.template_scope.is_let(name)) }
return true; dynamic_contextual_dependencies() {
if (is_reserved_keyword(name)) return Array.from(this.contextual_dependencies).filter(
return true; /** @param {any} name */ (name) => {
const variable = this.component.var_lookup.get(name); return Array.from(this.template_scope.dependencies_for_name.get(name)).some(
return is_dynamic(variable); /** @param {any} variable_name */
}); (variable_name) => {
} const variable = this.component.var_lookup.get(variable_name);
dynamic_contextual_dependencies() { return is_dynamic(variable);
return Array.from(this.contextual_dependencies).filter(/** @param {any} name */ (name) => { }
return Array.from(this.template_scope.dependencies_for_name.get(name)).some( );
}
/** @param {any} variable_name */ );
(variable_name) => { }
const variable = this.component.var_lookup.get(variable_name); // TODO move this into a render-dom wrapper?
return is_dynamic(variable);
}); /**
}); * @param {import('../../render_dom/Block.js').default} [block]
} * @param {string | void} [ctx]
// TODO move this into a render-dom wrapper? */
manipulate(block, ctx) {
/** // TODO ideally we wouldn't end up calling this method
* @param {import('../../render_dom/Block.js').default} [block] // multiple times
* @param {string | void} [ctx] if (this.manipulated) return this.manipulated;
*/ const { component, declarations, scope_map: map, template_scope, owner } = this;
manipulate(block, ctx) { let scope = this.scope;
// TODO ideally we wouldn't end up calling this method let function_expression;
// multiple times
if (this.manipulated) /** @type {Set<string>} */
return this.manipulated; let dependencies;
const { component, declarations, scope_map: map, template_scope, owner } = this;
let scope = this.scope; /** @type {Set<string>} */
let function_expression; let contextual_dependencies;
const node = walk(this.node, {
/** @type {Set<string>} */ /**
let dependencies; * @param {any} node
* @param {any} parent
/** @type {Set<string>} */ */
let contextual_dependencies; enter(node, parent) {
const node = walk(this.node, { if (node.type === 'Property' && node.shorthand) {
node.value = clone(node.value);
/** node.shorthand = false;
* @param {any} node }
* @param {any} parent if (map.has(node)) {
*/ scope = map.get(node);
enter(node, parent) { }
if (node.type === 'Property' && node.shorthand) { if (node.type === 'Identifier' && is_reference(node, parent)) {
node.value = clone(node.value); const { name } = flatten_reference(node);
node.shorthand = false; if (scope.has(name)) return;
} if (function_expression) {
if (map.has(node)) { if (template_scope.names.has(name)) {
scope = map.get(node); contextual_dependencies.add(name);
} template_scope.dependencies_for_name.get(name).forEach(
if (node.type === 'Identifier' && is_reference(node, parent)) { /** @param {any} dependency */ (dependency) => {
const { name } = flatten_reference(node); dependencies.add(dependency);
if (scope.has(name)) }
return; );
if (function_expression) { } else {
if (template_scope.names.has(name)) { dependencies.add(name);
contextual_dependencies.add(name); component.add_reference(node, name); // TODO is this redundant/misplaced?
template_scope.dependencies_for_name.get(name).forEach(/** @param {any} dependency */ (dependency) => { }
dependencies.add(dependency); } else if (is_contextual(component, template_scope, name)) {
}); const reference = block.renderer.reference(node, ctx);
} this.replace(reference);
else { }
dependencies.add(name); this.skip();
component.add_reference(node, name); // TODO is this redundant/misplaced? }
} if (!function_expression) {
} if (node.type === 'AssignmentExpression') {
else if (is_contextual(component, template_scope, name)) { // TODO should this be a warning/error? `<p>{foo = 1}</p>`
const reference = block.renderer.reference(node, ctx); }
this.replace(reference); if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
} function_expression = node;
this.skip(); dependencies = new Set();
} contextual_dependencies = new Set();
if (!function_expression) { }
if (node.type === 'AssignmentExpression') { }
// TODO should this be a warning/error? `<p>{foo = 1}</p>` },
}
if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') { /**
function_expression = node; * @param {import('estree').Node} node
dependencies = new Set(); * @param {import('estree').Node} parent
contextual_dependencies = new Set(); */
} leave(node, parent) {
} if (map.has(node)) scope = scope.parent;
}, if (node === function_expression) {
const id = component.get_unique_name(sanitize(get_function_name(node, owner)));
/** const declaration = b`const ${id} = ${node}`;
* @param {import('estree').Node} node const extract_functions = () => {
* @param {import('estree').Node} parent const deps = Array.from(contextual_dependencies);
*/ const function_expression = /** @type {import('estree').FunctionExpression} */ (node);
leave(node, parent) { const has_args = function_expression.params.length > 0;
if (map.has(node)) function_expression.params = [
scope = scope.parent; ...deps.map(
if (node === function_expression) { /** @param {any} name */ (name) =>
const id = component.get_unique_name(sanitize(get_function_name(node, owner))); /** @type {import('estree').Identifier} */ ({ type: 'Identifier', name })
const declaration = b `const ${id} = ${node}`; ),
const extract_functions = () => { ...function_expression.params
const deps = Array.from(contextual_dependencies); ];
const function_expression = /** @type {import('estree').FunctionExpression} */ (node); const context_args = deps.map(
const has_args = function_expression.params.length > 0; /** @param {any} name */ (name) => block.renderer.reference(name, ctx)
function_expression.params = [ );
...deps.map(/** @param {any} name */ (name) => ( /** @type {import('estree').Identifier} */({ type: 'Identifier', name }))), component.partly_hoisted.push(declaration);
...function_expression.params block.renderer.add_to_context(id.name);
]; const callee = block.renderer.reference(id);
const context_args = deps.map(/** @param {any} name */ (name) => block.renderer.reference(name, ctx)); this.replace(/** @type {any} */ (id));
component.partly_hoisted.push(declaration); const func_declaration = has_args
block.renderer.add_to_context(id.name); ? b`function ${id}(...args) {
const callee = block.renderer.reference(id);
this.replace(/** @type {any} */ (id));
const func_declaration = has_args
? b `function ${id}(...args) {
return ${callee}(${context_args}, ...args); return ${callee}(${context_args}, ...args);
}` }`
: b `function ${id}() { : b`function ${id}() {
return ${callee}(${context_args}); return ${callee}(${context_args});
}`; }`;
return { deps, func_declaration }; return { deps, func_declaration };
}; };
if (owner.type === 'ConstTag') { if (owner.type === 'ConstTag') {
// we need a combo block/init recipe // we need a combo block/init recipe
if (contextual_dependencies.size === 0) { if (contextual_dependencies.size === 0) {
let child_scope = scope; let child_scope = scope;
walk(node, { walk(node, {
/**
/** * @param {import('estree').Node} node
* @param {import('estree').Node} node * @param {any} parent
* @param {any} parent */
*/ enter(node, parent) {
enter(node, parent) { if (map.has(node)) child_scope = map.get(node);
if (map.has(node)) if (node.type === 'Identifier' && is_reference(node, parent)) {
child_scope = map.get(node); if (child_scope.has(node.name)) return;
if (node.type === 'Identifier' && is_reference(node, parent)) { this.replace(block.renderer.reference(node, ctx));
if (child_scope.has(node.name)) }
return; },
this.replace(block.renderer.reference(node, ctx));
} /** @param {import('estree').Node} node */
}, leave(node) {
if (map.has(node)) child_scope = child_scope.parent;
/** @param {import('estree').Node} node */ }
leave(node) { });
if (map.has(node)) } else {
child_scope = child_scope.parent; const { func_declaration } = extract_functions();
} this.replace(func_declaration[0]);
}); }
} } else if (dependencies.size === 0 && contextual_dependencies.size === 0) {
else { // we can hoist this out of the component completely
const { func_declaration } = extract_functions(); component.fully_hoisted.push(declaration);
this.replace(func_declaration[0]); this.replace(/** @type {any} */ (id));
} component.add_var(node, {
} name: id.name,
else if (dependencies.size === 0 && contextual_dependencies.size === 0) { internal: true,
// we can hoist this out of the component completely hoistable: true,
component.fully_hoisted.push(declaration); referenced: true
this.replace(/** @type {any} */ (id)); });
component.add_var(node, { } else if (contextual_dependencies.size === 0) {
name: id.name, // function can be hoisted inside the component init
internal: true, component.partly_hoisted.push(declaration);
hoistable: true, block.renderer.add_to_context(id.name);
referenced: true this.replace(block.renderer.reference(id));
}); } else {
} // we need a combo block/init recipe
else if (contextual_dependencies.size === 0) { const { deps, func_declaration } = extract_functions();
// function can be hoisted inside the component init if (owner.type === 'Attribute' && owner.parent.name === 'slot') {
component.partly_hoisted.push(declaration); const dep_scopes = new Set(
block.renderer.add_to_context(id.name); deps.map(/** @param {any} name */ (name) => template_scope.get_owner(name))
this.replace(block.renderer.reference(id)); );
} // find the nearest scopes
else {
// we need a combo block/init recipe /** @type {import('../interfaces.js').INode} */
const { deps, func_declaration } = extract_functions(); let node = owner.parent;
if (owner.type === 'Attribute' && owner.parent.name === 'slot') { while (node && !dep_scopes.has(node)) {
const dep_scopes = new Set(deps.map(/** @param {any} name */ (name) => template_scope.get_owner(name))); node = node.parent;
// find the nearest scopes }
const func_expression = func_declaration[0];
/** @type {import('../interfaces.js').INode} */ if (node.type === 'InlineComponent' || node.type === 'SlotTemplate') {
let node = owner.parent; // <Comp let:data />
while (node && !dep_scopes.has(node)) { this.replace(func_expression);
node = node.parent; } else {
} // {#each}, {#await}
const func_expression = func_declaration[0]; const func_id = component.get_unique_name(id.name + '_func');
if (node.type === 'InlineComponent' || node.type === 'SlotTemplate') { block.renderer.add_to_context(func_id.name, true);
// <Comp let:data /> // rename #ctx -> child_ctx;
this.replace(func_expression); walk(func_expression, {
} /** @param {import('estree').Node} node */
else { enter(node) {
// {#each}, {#await} if (node.type === 'Identifier' && node.name === '#ctx') {
const func_id = component.get_unique_name(id.name + '_func'); node.name = 'child_ctx';
block.renderer.add_to_context(func_id.name, true); }
// rename #ctx -> child_ctx; }
walk(func_expression, { });
// add to get_xxx_context
/** @param {import('estree').Node} node */ // child_ctx[x] = function () { ... }
enter(node) { /** @type {import('../EachBlock.js').default} */ (
if (node.type === 'Identifier' && node.name === '#ctx') { template_scope.get_owner(deps[0])
node.name = 'child_ctx'; ).contexts.push({
} type: 'DestructuredVariable',
} key: func_id,
}); modifier: () => func_expression,
// add to get_xxx_context default_modifier: /** @param {any} node */ (node) => node
// child_ctx[x] = function () { ... } });
( /** @type {import('../EachBlock.js').default} */(template_scope.get_owner(deps[0]))).contexts.push({ this.replace(block.renderer.reference(func_id));
type: 'DestructuredVariable', }
key: func_id, } else {
modifier: () => func_expression, declarations.push(func_declaration);
default_modifier: /** @param {any} node */ (node) => node }
}); }
this.replace(block.renderer.reference(func_id)); function_expression = null;
} dependencies = null;
} contextual_dependencies = null;
else { if (parent && parent.type === 'Property') {
declarations.push(func_declaration); parent.method = false;
} }
} }
function_expression = null; if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
dependencies = null; const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument;
contextual_dependencies = null; const object_name = get_object(assignee).name;
if (parent && parent.type === 'Property') { if (scope.has(object_name)) return;
parent.method = false; // normally (`a = 1`, `b.c = 2`), there'll be a single name
} // (a or b). In destructuring cases (`[d, e] = [e, d]`) there
} // may be more, in which case we need to tack the extra ones
if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') { // onto the initial function call
const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument; const names = new Set(extract_names(/** @type {import('estree').Node} */ (assignee)));
const object_name = get_object(assignee).name;
if (scope.has(object_name)) /** @type {Set<string>} */
return; const traced = new Set();
// normally (`a = 1`, `b.c = 2`), there'll be a single name names.forEach(
// (a or b). In destructuring cases (`[d, e] = [e, d]`) there /** @param {any} name */ (name) => {
// may be more, in which case we need to tack the extra ones const dependencies = template_scope.dependencies_for_name.get(name);
// onto the initial function call if (dependencies) {
const names = new Set(extract_names(/** @type {import('estree').Node} */ (assignee))); dependencies.forEach(/** @param {any} name */ (name) => traced.add(name));
} else {
/** @type {Set<string>} */ traced.add(name);
const traced = new Set(); }
names.forEach(/** @param {any} name */ (name) => { }
const dependencies = template_scope.dependencies_for_name.get(name); );
if (dependencies) { const context = block.bindings.get(object_name);
dependencies.forEach(/** @param {any} name */ (name) => traced.add(name)); if (context) {
} // for `{#each array as item}`
else { // replace `item = 1` to `each_array[each_index] = 1`, this allow us to mutate the array
traced.add(name); // rather than mutating the local `item` variable
} const { snippet, object, property } = context;
});
const context = block.bindings.get(object_name); /** @type {any} */
if (context) { const replaced = replace_object(assignee, snippet);
// for `{#each array as item}` if (node.type === 'AssignmentExpression') {
// replace `item = 1` to `each_array[each_index] = 1`, this allow us to mutate the array node.left = replaced;
// rather than mutating the local `item` variable } else {
const { snippet, object, property } = context; node.argument = replaced;
}
/** @type {any} */ contextual_dependencies.add(object.name);
const replaced = replace_object(assignee, snippet); contextual_dependencies.add(property.name);
if (node.type === 'AssignmentExpression') { }
node.left = replaced; this.replace(invalidate(block.renderer, scope, node, traced));
} }
else { }
node.argument = replaced; });
} if (declarations.length > 0) {
contextual_dependencies.add(object.name); block.maintain_context = true;
contextual_dependencies.add(property.name); declarations.forEach(
} /** @param {any} declaration */ (declaration) => {
this.replace(invalidate(block.renderer, scope, node, traced)); block.chunks.init.push(declaration);
} }
} );
}); }
if (declarations.length > 0) { return (this.manipulated = /** @type {import('estree').Node} */ (node));
block.maintain_context = true; }
declarations.forEach(/** @param {any} declaration */ (declaration) => {
block.chunks.init.push(declaration);
});
}
return (this.manipulated = /** @type {import('estree').Node} */ (node));
}
} }
/** /**
@ -482,15 +472,13 @@ export default class Expression {
* @param {any} parent * @param {any} parent
*/ */
function get_function_name(_node, parent) { function get_function_name(_node, parent) {
if (parent.type === 'EventHandler') { if (parent.type === 'EventHandler') {
return `${parent.name}_handler`; return `${parent.name}_handler`;
} }
if (parent.type === 'Action') { if (parent.type === 'Action') {
return `${parent.name}_function`; return `${parent.name}_function`;
} }
return 'func'; return 'func';
} }
/** @typedef {INode} Owner */ /** @typedef {INode} Owner */

Loading…
Cancel
Save