use super::declarations::write_decorators_field;
use super::expressions::{write_expression, write_expressions};
use super::functions::write_function_expression;
use super::{
Ctx, JsonWriter, close_node, node_header, node_header_wide_end, write_array,
write_type_annotation_field,
};
use crate::ast;
use tsv_lang::Span;
pub(super) fn pattern_header(
w: &mut JsonWriter,
node_type: &'static str,
span: Span,
type_annotation: Option<&ast::TSTypeAnnotation<'_>>,
ctx: &Ctx<'_>,
) {
match type_annotation {
Some(ta) => node_header_wide_end(w, node_type, span, ta.span.end, ctx),
None => node_header(w, node_type, span, ctx),
}
}
pub(super) fn write_template_literal(
w: &mut JsonWriter,
template: &ast::TemplateLiteral<'_>,
ctx: &Ctx<'_>,
) {
node_header(w, "TemplateLiteral", template.span, ctx);
w.raw(",\"expressions\":");
write_expressions(w, template.expressions.iter().copied(), ctx);
w.raw(",\"quasis\":");
write_array(w, template.quasis, |w, q| write_template_element(w, q, ctx));
close_node(w, "TemplateLiteral", template.span, ctx);
}
pub(super) fn write_template_element(
w: &mut JsonWriter,
element: &ast::TemplateElement<'_>,
ctx: &Ctx<'_>,
) {
let adjusted_start = element.span.start + 1;
let adjusted_end = if element.tail {
element.span.end - 1
} else {
element.span.end - 2
};
let adjusted_span = Span::new(adjusted_start, adjusted_end);
node_header(w, "TemplateElement", adjusted_span, ctx);
w.raw(",\"value\":{\"raw\":");
w.string(element.trv(ctx.source));
w.raw(",\"cooked\":");
match element.cooked {
ast::TemplateCooked::Verbatim => w.string(element.trv(ctx.source)),
ast::TemplateCooked::Decoded(decoded) => w.string(decoded),
ast::TemplateCooked::Invalid => w.null(),
}
w.raw_pick(element.tail, b"},\"tail\":true", b"},\"tail\":false");
close_node(w, "TemplateElement", adjusted_span, ctx);
}
pub(super) fn write_object_pattern(
w: &mut JsonWriter,
obj: &ast::ObjectPattern<'_>,
span: Span,
ctx: &Ctx<'_>,
) {
pattern_header(w, "ObjectPattern", span, obj.type_annotation.as_ref(), ctx);
w.raw(",\"properties\":");
write_array(w, obj.properties, |w, p| match p {
ast::ObjectPatternProperty::Property(p) => write_property(w, p, ctx),
ast::ObjectPatternProperty::RestElement(r) => write_rest_element(w, r, ctx),
});
if obj.optional {
w.raw(",\"optional\":true");
}
write_type_annotation_field(w, obj.type_annotation.as_ref(), ctx);
write_decorators_field(w, obj.decorators, ctx);
close_node(w, "ObjectPattern", span, ctx);
}
pub(super) fn write_rest_element(w: &mut JsonWriter, rest: &ast::RestElement<'_>, ctx: &Ctx<'_>) {
node_header(w, "RestElement", rest.span, ctx);
w.raw(",\"argument\":");
write_expression(w, rest.argument, ctx);
if rest.optional {
w.raw(",\"optional\":true");
}
write_type_annotation_field(w, rest.type_annotation.as_ref(), ctx);
close_node(w, "RestElement", rest.span, ctx);
}
pub(super) fn write_property(w: &mut JsonWriter, prop: &ast::Property<'_>, ctx: &Ctx<'_>) {
node_header(w, "Property", prop.span, ctx);
if prop.method || prop.computed {
w.raw_pick(prop.method, b",\"method\":true", b",\"method\":false");
w.raw_pick(
prop.shorthand,
b",\"shorthand\":true",
b",\"shorthand\":false",
);
w.raw_pick(
prop.computed,
b",\"computed\":true,\"key\":",
b",\"computed\":false,\"key\":",
);
} else {
w.raw_pick(
prop.shorthand,
b",\"method\":false,\"shorthand\":true,\"computed\":false,\"key\":",
b",\"method\":false,\"shorthand\":false,\"computed\":false,\"key\":",
);
}
write_expression(w, prop.key, ctx);
let getset = !matches!(prop.kind, ast::PropertyKind::Init);
let generic_method = prop.method
&& matches!(&prop.value.kind, ast::ExpressionKind::FunctionExpression(f)
if f.type_parameters.is_some());
if (getset || generic_method) && !ctx.vanilla_acorn {
w.raw(",\"kind\":\"");
w.raw(prop.kind.as_str());
w.raw("\",\"value\":");
write_property_value(w, prop, ctx);
} else {
w.raw(",\"value\":");
write_property_value(w, prop, ctx);
match prop.kind {
ast::PropertyKind::Init => w.raw(",\"kind\":\"init\""),
kind => {
w.raw(",\"kind\":\"");
w.raw(kind.as_str());
w.raw("\"");
}
}
}
close_node(w, "Property", prop.span, ctx);
}
fn write_property_value(w: &mut JsonWriter, prop: &ast::Property<'_>, ctx: &Ctx<'_>) {
let method_value = prop.method || !matches!(prop.kind, ast::PropertyKind::Init);
match (&prop.value, method_value) {
(
ast::Expression {
kind: ast::ExpressionKind::FunctionExpression(f),
..
},
true,
) => {
write_function_expression(w, f, ctx, true);
}
(
ast::Expression {
kind: ast::ExpressionKind::AssignmentExpression(assign),
span,
},
false,
) if prop.shorthand => {
debug_assert_eq!(assign.operator, ast::AssignmentOperator::Assign);
node_header(w, "AssignmentPattern", *span, ctx);
w.raw(",\"left\":");
write_expression(w, assign.left, ctx);
w.raw(",\"right\":");
write_expression(w, assign.right, ctx);
close_node(w, "AssignmentPattern", *span, ctx);
}
_ => write_expression(w, prop.value, ctx),
}
}
pub(super) fn write_assignment_pattern(
w: &mut JsonWriter,
pattern: &ast::AssignmentPattern<'_>,
ctx: &Ctx<'_>,
span: Span,
) {
node_header(w, "AssignmentPattern", span, ctx);
w.raw(",\"left\":");
write_expression(w, pattern.left, ctx);
w.raw(",\"right\":");
write_expression(w, pattern.right, ctx);
write_decorators_field(w, pattern.decorators, ctx);
close_node(w, "AssignmentPattern", span, ctx);
}