mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(napi/parser): utf16 span for module record (#9093)
I (and mostly copilot) wrote `VisitMutModuleRecord` to visit all spans as I wasn't sure how `VisitMut` is generated. I'm looking into visit generator now if it's something I can do. Please let me know if manually writing it down is fine for now or I should try macro (or maybe a completely different approach).
This commit is contained in:
1 parent
329de9478a
commit
15f23f1556
5 files changed
+180
-6
No files matched your search
@@ -1,6 +1,7 @@
|
||||
//! Convert UTF-8 span offsets to UTF-16.
|
||||
|
||||
use oxc_span::Span;
|
||||
use oxc_syntax::module_record::{ModuleRecord, VisitMutModuleRecord};
|
||||
|
||||
use crate::{ast::Program, visit::VisitMut};
|
||||
|
||||
@@ -41,6 +42,11 @@ impl Utf8ToUtf16 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert spans in ModuleRecord to UTF-16
|
||||
pub fn convert_module_record(&mut self, module_record: &mut ModuleRecord<'_>) {
|
||||
self.visit_module_record(module_record);
|
||||
}
|
||||
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
fn build_table(&mut self, source_text: &str) {
|
||||
// Translation from UTF-8 byte offset to UTF-16 char offset:
|
||||
@@ -102,6 +108,12 @@ impl VisitMut<'_> for Utf8ToUtf16 {
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMutModuleRecord for Utf8ToUtf16 {
|
||||
fn visit_span(&mut self, span: &mut Span) {
|
||||
self.convert_span(span);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_allocator::Allocator;
|
||||
|
||||
@@ -387,6 +387,87 @@ pub struct DynamicImport {
|
||||
pub module_request: Span,
|
||||
}
|
||||
|
||||
#[expect(missing_docs)]
|
||||
pub trait VisitMutModuleRecord {
|
||||
fn visit_module_record(&mut self, module_record: &mut ModuleRecord) {
|
||||
module_record.requested_modules.values_mut().for_each(|e| {
|
||||
e.iter_mut().for_each(|e| self.visit_requested_module(e));
|
||||
});
|
||||
module_record.import_entries.iter_mut().for_each(|e| self.visit_import_entry(e));
|
||||
module_record.local_export_entries.iter_mut().for_each(|e| self.visit_export_entry(e));
|
||||
module_record.indirect_export_entries.iter_mut().for_each(|e| self.visit_export_entry(e));
|
||||
module_record.star_export_entries.iter_mut().for_each(|e| self.visit_export_entry(e));
|
||||
module_record.dynamic_imports.iter_mut().for_each(|e| self.visit_dynamic_import(e));
|
||||
module_record.import_metas.iter_mut().for_each(|e| self.visit_span(e));
|
||||
}
|
||||
|
||||
fn visit_requested_module(&mut self, requested_module: &mut RequestedModule) {
|
||||
self.visit_span(&mut requested_module.span);
|
||||
self.visit_span(&mut requested_module.statement_span);
|
||||
}
|
||||
|
||||
fn visit_import_entry(&mut self, import_entry: &mut ImportEntry) {
|
||||
self.visit_span(&mut import_entry.statement_span);
|
||||
self.visit_name_span(&mut import_entry.module_request);
|
||||
self.visit_name_span(&mut import_entry.local_name);
|
||||
self.visit_import_import_name(&mut import_entry.import_name);
|
||||
}
|
||||
|
||||
fn visit_import_import_name(&mut self, import_import_name: &mut ImportImportName) {
|
||||
match import_import_name {
|
||||
ImportImportName::Name(name_span) => self.visit_name_span(name_span),
|
||||
ImportImportName::NamespaceObject => {}
|
||||
ImportImportName::Default(span) => self.visit_span(span),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_export_entry(&mut self, export_entry: &mut ExportEntry) {
|
||||
self.visit_span(&mut export_entry.statement_span);
|
||||
self.visit_span(&mut export_entry.span);
|
||||
if let Some(module_request) = &mut export_entry.module_request {
|
||||
self.visit_name_span(module_request);
|
||||
}
|
||||
self.visit_export_import_name(&mut export_entry.import_name);
|
||||
self.visit_export_export_name(&mut export_entry.export_name);
|
||||
self.visit_export_local_name(&mut export_entry.local_name);
|
||||
}
|
||||
|
||||
fn visit_export_import_name(&mut self, export_import_name: &mut ExportImportName) {
|
||||
match export_import_name {
|
||||
ExportImportName::Name(name_span) => self.visit_name_span(name_span),
|
||||
ExportImportName::All | ExportImportName::AllButDefault | ExportImportName::Null => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_export_export_name(&mut self, export_export_name: &mut ExportExportName) {
|
||||
match export_export_name {
|
||||
ExportExportName::Name(name_span) => self.visit_name_span(name_span),
|
||||
ExportExportName::Default(span) => self.visit_span(span),
|
||||
ExportExportName::Null => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_export_local_name(&mut self, export_local_name: &mut ExportLocalName) {
|
||||
match export_local_name {
|
||||
ExportLocalName::Name(name_span) | ExportLocalName::Default(name_span) => {
|
||||
self.visit_name_span(name_span);
|
||||
}
|
||||
ExportLocalName::Null => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_dynamic_import(&mut self, dynamic_import: &mut DynamicImport) {
|
||||
self.visit_span(&mut dynamic_import.module_request);
|
||||
self.visit_span(&mut dynamic_import.span);
|
||||
}
|
||||
|
||||
fn visit_name_span(&mut self, name_span: &mut NameSpan) {
|
||||
self.visit_span(&mut name_span.span);
|
||||
}
|
||||
|
||||
fn visit_span(&mut self, _span: &mut Span) {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_span::Span;
|
||||
|
||||
@@ -86,16 +86,15 @@ fn parse_with_return(filename: &str, source_text: String, options: &ParserOption
|
||||
})
|
||||
.collect::<Vec<Comment>>();
|
||||
|
||||
let module = EcmaScriptModule::from(&ret.module_record);
|
||||
|
||||
if options.convert_span_utf16.unwrap_or(false) {
|
||||
// TODO: fix spans in `module_record` and `errors`
|
||||
// TODO: fix spans in `errors`
|
||||
|
||||
// Empty `comments` so comment spans don't get converted twice
|
||||
ret.program.comments.clear();
|
||||
|
||||
let mut converter = Utf8ToUtf16::new();
|
||||
converter.convert(&mut ret.program);
|
||||
converter.convert_module_record(&mut ret.module_record);
|
||||
|
||||
for comment in &mut comments {
|
||||
comment.start = converter.convert_offset(comment.start);
|
||||
@@ -103,7 +102,7 @@ fn parse_with_return(filename: &str, source_text: String, options: &ParserOption
|
||||
}
|
||||
}
|
||||
let program = serde_json::to_string(&ret.program).unwrap();
|
||||
|
||||
let module = EcmaScriptModule::from(&ret.module_record);
|
||||
ParseResult { source_text, program, module, comments, errors }
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,90 @@ it('utf16 span', async () => {
|
||||
]
|
||||
`);
|
||||
}
|
||||
{
|
||||
const ret = await parseAsync('test.js', `"🤨";import x from "x"; export { x };import("y");import.meta.z`, {
|
||||
convertSpanUtf16: true,
|
||||
});
|
||||
expect(ret.module).toMatchInlineSnapshot(`
|
||||
{
|
||||
"dynamicImports": [
|
||||
{
|
||||
"end": 48,
|
||||
"moduleRequest": {
|
||||
"end": 47,
|
||||
"start": 44,
|
||||
},
|
||||
"start": 37,
|
||||
},
|
||||
],
|
||||
"hasModuleSyntax": true,
|
||||
"importMetas": [
|
||||
{
|
||||
"end": 60,
|
||||
"start": 49,
|
||||
},
|
||||
],
|
||||
"staticExports": [
|
||||
{
|
||||
"end": 23,
|
||||
"entries": [
|
||||
{
|
||||
"end": 34,
|
||||
"exportName": {
|
||||
"end": 34,
|
||||
"kind": "Name",
|
||||
"name": "x",
|
||||
"start": 33,
|
||||
},
|
||||
"importName": {
|
||||
"end": 13,
|
||||
"kind": "Name",
|
||||
"name": "x",
|
||||
"start": 12,
|
||||
},
|
||||
"localName": {
|
||||
"kind": "None",
|
||||
},
|
||||
"moduleRequest": {
|
||||
"end": 22,
|
||||
"start": 19,
|
||||
"value": "x",
|
||||
},
|
||||
"start": 33,
|
||||
},
|
||||
],
|
||||
"start": 5,
|
||||
},
|
||||
],
|
||||
"staticImports": [
|
||||
{
|
||||
"end": 23,
|
||||
"entries": [
|
||||
{
|
||||
"importName": {
|
||||
"end": 13,
|
||||
"kind": "Default",
|
||||
"start": 12,
|
||||
},
|
||||
"isType": false,
|
||||
"localName": {
|
||||
"end": 13,
|
||||
"start": 12,
|
||||
"value": "x",
|
||||
},
|
||||
},
|
||||
],
|
||||
"moduleRequest": {
|
||||
"end": 22,
|
||||
"start": 19,
|
||||
"value": "x",
|
||||
},
|
||||
"start": 5,
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
|
||||
Generated
-2
@@ -112,8 +112,6 @@ importers:
|
||||
|
||||
npm/runtime: {}
|
||||
|
||||
npm/wasm-web: {}
|
||||
|
||||
tasks/benchmark/codspeed:
|
||||
devDependencies:
|
||||
axios:
|
||||
|
||||
Reference in new issue
Block a user