fix: detect preprocessor in re-exported named exports (#6899)

Fixes #6894

The TypeScript parser now correctly detects preprocessor functions
that are re-exported from other modules using named exports like:
  export { preprocessor } from "./other_module";

Previously, only function declarations were detected. Now the parser
also checks ExportNamed AST nodes for any specifier named 'preprocessor'.

This allows developers to easily reuse preprocessor functions across
multiple scripts without the workaround of wrapping them in a new function.

Added comprehensive tests covering:
- Simple re-export: export { preprocessor } from "./other"
- Re-export with renaming: export { preprocessor as preprocessor }
- Mixed exports: export { foo, preprocessor, bar }
- Negative case: exports without preprocessor
This commit is contained in:
Vivek Chavan
2025-10-23 13:21:57 +05:30
committed by GitHub
parent 034cc67965
commit 16f7afac48
2 changed files with 94 additions and 0 deletions
@@ -255,6 +255,28 @@ pub fn parse_deno_signature(
let mut symbol_table: HashMap<String, TypeDecl> = HashMap::new();
for item in ast {
// Check for named exports (e.g., export { preprocessor } from "./other")
if let ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(named_export)) = &item {
if !has_preprocessor {
for specifier in &named_export.specifiers {
if let swc_ecma_ast::ExportSpecifier::Named(spec) = specifier {
let export_name = match &spec.exported {
Some(swc_ecma_ast::ModuleExportName::Ident(ident)) => ident.sym.as_ref(),
Some(swc_ecma_ast::ModuleExportName::Str(s)) => s.value.as_ref(),
None => match &spec.orig {
swc_ecma_ast::ModuleExportName::Ident(ident) => ident.sym.as_ref(),
swc_ecma_ast::ModuleExportName::Str(s) => s.value.as_ref(),
},
};
if export_name == "preprocessor" {
has_preprocessor = true;
break;
}
}
}
}
}
if let ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { decl, .. }))
| ModuleItem::Stmt(Stmt::Decl(decl)) = item
{
@@ -685,4 +685,76 @@ mod tests {
}
);
}
#[test]
fn test_parse_with_preprocessor_reexport() {
// Test case for issue #6894: preprocessor re-export should be detected
let code = r#"
export { preprocessor } from "./extract_user_info_from_jwt_token";
export async function main(param: string) {
return param;
}
"#;
let sig = parse_deno_signature(code, false, false, None).unwrap();
assert_eq!(
sig,
MainArgSignature {
star_args: false,
star_kwargs: false,
args: vec![Arg {
name: "param".to_string(),
otyp: None,
typ: Typ::Str(None),
default: None,
has_default: false,
oidx: None,
}],
no_main_func: Some(false),
has_preprocessor: Some(true),
}
);
}
#[test]
fn test_parse_with_preprocessor_reexport_renamed() {
// Test case for renamed re-exports
let code = r#"
export { preprocessor as preprocessor } from "./other";
export async function main(param: string) {
return param;
}
"#;
let sig = parse_deno_signature(code, false, false, None).unwrap();
assert_eq!(sig.has_preprocessor, Some(true));
}
#[test]
fn test_parse_with_preprocessor_among_other_exports() {
// Test case where preprocessor is one of many exports
let code = r#"
export { foo, preprocessor, bar } from "./utils";
export async function main(param: string) {
return param;
}
"#;
let sig = parse_deno_signature(code, false, false, None).unwrap();
assert_eq!(sig.has_preprocessor, Some(true));
}
#[test]
fn test_parse_without_preprocessor_other_exports() {
// Test case where there are exports but no preprocessor
let code = r#"
export { foo, bar } from "./utils";
export async function main(param: string) {
return param;
}
"#;
let sig = parse_deno_signature(code, false, false, None).unwrap();
assert_eq!(sig.has_preprocessor, Some(false));
}
}