#![allow(dead_code)]
#![allow(unused_imports)]
use oxc_ast::ast::*;
use oxc_span::GetSpan;
use super::types::{ScriptBinding, ScriptExport, ScriptImport, ScriptItem};
use crate::common::Span;
pub struct ScriptParseContext<'a> {
pub base_offset: u32,
pub source_bytes: &'a [u8],
}
impl<'a> ScriptParseContext<'a> {
pub fn new(base_offset: u32, source_bytes: &'a [u8]) -> Self {
Self {
base_offset,
source_bytes,
}
}
#[inline]
pub fn adjust_span(&self, oxc_span: oxc_span::Span) -> Span {
Span::new(
oxc_span.start + self.base_offset,
oxc_span.end + self.base_offset,
)
}
#[inline]
pub fn slice_str(&self, start: u32, end: u32) -> &'a str {
unsafe { std::str::from_utf8_unchecked(&self.source_bytes[start as usize..end as usize]) }
}
}
pub fn process_import<'a>(
import: &ImportDeclaration<'a>,
ctx: &ScriptParseContext<'a>,
) -> ScriptImport<'a> {
let mut bindings = Vec::new();
if let Some(specifiers) = &import.specifiers {
for spec in specifiers {
match spec {
ImportDeclarationSpecifier::ImportSpecifier(s) => {
bindings.push(ScriptBinding {
name: s.local.name.as_str(),
span: ctx.adjust_span(s.local.span),
});
}
ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => {
bindings.push(ScriptBinding {
name: s.local.name.as_str(),
span: ctx.adjust_span(s.local.span),
});
}
ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => {
bindings.push(ScriptBinding {
name: s.local.name.as_str(),
span: ctx.adjust_span(s.local.span),
});
}
}
}
}
ScriptImport {
span: ctx.adjust_span(import.span),
source: import.source.value.as_str(),
source_span: ctx.adjust_span(import.source.span),
bindings,
is_type_only: import.import_kind.is_type(),
}
}
pub fn process_named_export<'a>(
export: &ExportNamedDeclaration<'a>,
ctx: &ScriptParseContext<'a>,
) -> ScriptExport<'a> {
let mut bindings = Vec::new();
for spec in &export.specifiers {
let name = match &spec.exported {
ModuleExportName::IdentifierName(id) => id.name.as_str(),
ModuleExportName::IdentifierReference(id) => id.name.as_str(),
ModuleExportName::StringLiteral(s) => s.value.as_str(),
};
bindings.push(ScriptBinding {
name,
span: ctx.adjust_span(spec.exported.span()),
});
}
if let Some(decl) = &export.declaration {
extract_declaration_bindings(decl, ctx, &mut bindings);
}
ScriptExport {
span: ctx.adjust_span(export.span),
bindings,
source: export.source.as_ref().map(|s| s.value.as_str()),
is_type_only: export.export_kind.is_type(),
}
}
pub fn process_all_export<'a>(
export: &ExportAllDeclaration<'a>,
ctx: &ScriptParseContext<'a>,
) -> ScriptExport<'a> {
let bindings = if let Some(exported) = &export.exported {
let name = match exported {
ModuleExportName::IdentifierName(id) => id.name.as_str(),
ModuleExportName::IdentifierReference(id) => id.name.as_str(),
ModuleExportName::StringLiteral(s) => s.value.as_str(),
};
vec![ScriptBinding {
name,
span: ctx.adjust_span(exported.span()),
}]
} else {
Vec::new()
};
ScriptExport {
span: ctx.adjust_span(export.span),
bindings,
source: Some(export.source.value.as_str()),
is_type_only: export.export_kind.is_type(),
}
}
fn extract_declaration_bindings<'a>(
decl: &Declaration<'a>,
ctx: &ScriptParseContext<'a>,
bindings: &mut Vec<ScriptBinding<'a>>,
) {
match decl {
Declaration::VariableDeclaration(var_decl) => {
for declarator in &var_decl.declarations {
collect_binding_pattern_names(&declarator.id, ctx, bindings);
}
}
Declaration::FunctionDeclaration(func) => {
if let Some(id) = &func.id {
bindings.push(ScriptBinding {
name: id.name.as_str(),
span: ctx.adjust_span(id.span),
});
}
}
Declaration::ClassDeclaration(class) => {
if let Some(id) = &class.id {
bindings.push(ScriptBinding {
name: id.name.as_str(),
span: ctx.adjust_span(id.span),
});
}
}
Declaration::TSTypeAliasDeclaration(_)
| Declaration::TSInterfaceDeclaration(_)
| Declaration::TSEnumDeclaration(_)
| Declaration::TSModuleDeclaration(_)
| Declaration::TSImportEqualsDeclaration(_)
| Declaration::TSGlobalDeclaration(_) => {}
}
}
fn collect_binding_pattern_names<'a>(
pattern: &BindingPattern<'a>,
ctx: &ScriptParseContext<'a>,
bindings: &mut Vec<ScriptBinding<'a>>,
) {
match pattern {
BindingPattern::BindingIdentifier(id) => {
bindings.push(ScriptBinding {
name: id.name.as_str(),
span: ctx.adjust_span(id.span),
});
}
BindingPattern::ObjectPattern(obj) => {
for prop in &obj.properties {
collect_binding_pattern_names(&prop.value, ctx, bindings);
}
if let Some(rest) = &obj.rest {
collect_binding_pattern_names(&rest.argument, ctx, bindings);
}
}
BindingPattern::ArrayPattern(arr) => {
for elem in arr.elements.iter().flatten() {
collect_binding_pattern_names(elem, ctx, bindings);
}
if let Some(rest) = &arr.rest {
collect_binding_pattern_names(&rest.argument, ctx, bindings);
}
}
BindingPattern::AssignmentPattern(assign) => {
collect_binding_pattern_names(&assign.left, ctx, bindings);
}
}
}
pub fn try_process_import<'a>(
stmt: &Statement<'a>,
ctx: &ScriptParseContext<'a>,
) -> Option<ScriptItem<'a>> {
match stmt {
Statement::ImportDeclaration(import) => {
Some(ScriptItem::Import(process_import(import, ctx)))
}
_ => None,
}
}
pub fn try_process_export<'a>(
stmt: &Statement<'a>,
ctx: &ScriptParseContext<'a>,
) -> Option<ScriptItem<'a>> {
match stmt {
Statement::ExportNamedDeclaration(export) => {
Some(ScriptItem::Export(process_named_export(export, ctx)))
}
Statement::ExportAllDeclaration(export) => {
Some(ScriptItem::Export(process_all_export(export, ctx)))
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_adjust_span() {
let ctx = ScriptParseContext::new(100, b"const x = 1;");
let oxc_span = oxc_span::Span::new(0, 12);
let adjusted = ctx.adjust_span(oxc_span);
assert_eq!(adjusted.start, 100);
assert_eq!(adjusted.end, 112);
}
}