You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte/src/compiler/compile/nodes/shared/Context.ts

97 lines
3.1 KiB

import { x } from 'code-red';
import { Node, Identifier, Expression } from 'estree';
import { walk } from 'estree-walker';
import is_reference from 'is-reference';
import { clone } from '../../../utils/clone';
export interface Context {
key: Identifier;
name?: string;
modifier: (node: Node) => Node;
default_modifier: (node: Node, to_ctx: (name: string) => Node) => Node;
}
export function unpack_destructuring(contexts: Context[], node: Node, modifier: Context['modifier'] = node => node, default_modifier: Context['default_modifier'] = node => node) {
if (!node) return;
if (node.type === 'Identifier') {
contexts.push({
key: node as Identifier,
modifier,
default_modifier
});
} else if (node.type === 'RestElement') {
contexts.push({
key: node.argument as Identifier,
modifier,
default_modifier
});
} else if (node.type === 'ArrayPattern') {
node.elements.forEach((element, i) => {
if (element && element.type === 'RestElement') {
unpack_destructuring(contexts, element, node => x`${modifier(node)}.slice(${i})` as Node, default_modifier);
} else if (element && element.type === 'AssignmentPattern') {
const n = contexts.length;
unpack_destructuring(contexts, element.left, node => x`${modifier(node)}[${i}]`, (node, to_ctx) => x`${node} !== undefined ? ${node} : ${update_reference(contexts, n, element.right, to_ctx)}` as Node);
} else {
unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node, default_modifier);
}
});
} else if (node.type === 'ObjectPattern') {
const used_properties = [];
node.properties.forEach((property) => {
if (property.type === 'RestElement') {
unpack_destructuring(
contexts,
property.argument,
node => x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node,
default_modifier
);
} else {
const key = property.key as Identifier;
const value = property.value;
used_properties.push(x`"${key.name}"`);
if (value.type === 'AssignmentPattern') {
const n = contexts.length;
unpack_destructuring(contexts, value.left, node => x`${modifier(node)}.${key.name}`, (node, to_ctx) => x`${node} !== undefined ? ${node} : ${update_reference(contexts, n, value.right, to_ctx)}` as Node);
} else {
unpack_destructuring(contexts, value, node => x`${modifier(node)}.${key.name}` as Node, default_modifier);
}
}
});
}
}
function update_reference(contexts: Context[], n: number, expression: Expression, to_ctx: (name: string) => Node): Node {
const find_from_context = (node: Identifier) => {
for (let i = n; i < contexts.length; i++) {
const { key } = contexts[i];
if (node.name === key.name) {
throw new Error(`Cannot access '${node.name}' before initialization`);
}
}
return to_ctx(node.name);
};
if (expression.type === 'Identifier') {
return find_from_context(expression);
}
// NOTE: avoid unnecessary deep clone?
expression = clone(expression) as Expression;
walk(expression, {
enter(node, parent: Node) {
if (is_reference(node, parent)) {
this.replace(find_from_context(node as Identifier));
this.skip();
}
}
});
return expression;
}