Skip to content
This repository has been archived by the owner on Nov 24, 2024. It is now read-only.

Commit

Permalink
do not remove hooks in other components
Browse files Browse the repository at this point in the history
closes #3
  • Loading branch information
KubaJastrz committed Jun 28, 2024
1 parent b58830d commit a760fa6
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 22 deletions.
114 changes: 92 additions & 22 deletions src/codemod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use oxc_allocator::Allocator;
use oxc_ast::{
ast::{
AssignmentTarget, BindingPatternKind, Declaration, ExportDefaultDeclaration,
ExportDefaultDeclarationKind, ExportNamedDeclaration, Expression, VariableDeclaration,
ExportDefaultDeclarationKind, ExportNamedDeclaration, Expression, FunctionBody, Statement,
VariableDeclaration,
},
AstKind,
};
Expand Down Expand Up @@ -44,7 +45,7 @@ pub fn codemod(original_source_text: &String, source_type: SourceType) -> Result

let mut code_fixes = vec![];
let mut route_module_properties = vec![];
let mut hook_declarators = vec![];
let mut hook_declarators: Vec<HookDeclarator> = vec![];

// TODO: add headers
let known_remix_functions_with_args = vec![
Expand All @@ -64,36 +65,42 @@ pub fn codemod(original_source_text: &String, source_type: SourceType) -> Result
let type_annotations =
get_named_export_function_args_type_annotations(&named_export);
for span in type_annotations.iter() {
code_fixes.push(Fix::delete(*span));
code_fixes.push(Fix::delete(span.clone()));
}
}
}
}
AstKind::VariableDeclaration(var_decl) => {
if let Some((whole_declaration, declarator_id)) =
find_hook_usage(var_decl, "useLoaderData")
{
code_fixes.push(Fix::delete_with_leading_whitespace(whole_declaration));
hook_declarators.push(HookDeclarator {
name: "loaderData",
source_text: declarator_id.source_text(original_source_text),
});
}
if let Some((whole_declaration, declarator_id)) =
find_hook_usage(var_decl, "useActionData")
{
code_fixes.push(Fix::delete_with_leading_whitespace(whole_declaration));
hook_declarators.push(HookDeclarator {
name: "actionData",
source_text: declarator_id.source_text(original_source_text),
});
AstKind::ExportDefaultDeclaration(default_export) => {
match &default_export.declaration {
ExportDefaultDeclarationKind::FunctionDeclaration(decl) => {
if let Some(body) = &decl.body {
get_hook_declarators(&body, &original_source_text)
.iter()
.for_each(|(hook, span)| {
code_fixes
.push(Fix::delete_with_leading_whitespace(span.clone()));
hook_declarators.push(hook.clone());
});
}
}
ExportDefaultDeclarationKind::ArrowFunctionExpression(decl) => {
get_hook_declarators(&decl.body, &original_source_text)
.iter()
.for_each(|(hook, span)| {
code_fixes.push(Fix::delete_with_leading_whitespace(span.clone()));
hook_declarators.push(hook.clone());
});
}
_ => {}
}
}
_ => {}
}
}

let source_text = Fixer::new(&original_source_text, code_fixes).fix().fixed_code;
let source_text = Fixer::new(&original_source_text, code_fixes)
.fix()
.fixed_code;

//==========================================================================
// Second pass
Expand Down Expand Up @@ -173,6 +180,9 @@ pub fn codemod(original_source_text: &String, source_type: SourceType) -> Result
}
}

// If there are no known remix exports, return the original source text.
// It's fine that this check is after the second pass, as most route files
// will have at least one known remix export.
if route_module_properties.len() == 0 {
return Ok(original_source_text.to_string());
}
Expand Down Expand Up @@ -429,6 +439,44 @@ fn get_default_export_property<'a>(
}
}

fn get_hook_declarators<'a>(
function_body: &'a FunctionBody,
source_text: &'a str,
) -> Vec<(HookDeclarator<'a>, Span)> {
function_body
.statements
.iter()
.filter_map(|f| match f {
Statement::VariableDeclaration(var_decl) => {
if let Some((whole_declaration, declarator_id)) =
find_hook_usage(var_decl, "useLoaderData")
{
return Some((
HookDeclarator {
name: "loaderData",
source_text: declarator_id.source_text(source_text),
},
whole_declaration,
));
} else if let Some((whole_declaration, declarator_id)) =
find_hook_usage(var_decl, "useActionData")
{
return Some((
HookDeclarator {
name: "actionData",
source_text: declarator_id.source_text(source_text),
},
whole_declaration,
));
} else {
None
}
}
_ => None,
})
.collect::<Vec<_>>()
}

fn rename_exports<'a>(old_name: Option<&'a str>) -> Option<&str> {
match old_name {
Some("loader") => Some("serverLoader"),
Expand Down Expand Up @@ -828,6 +876,28 @@ mod tests {
assert_snapshot("component_client_loader_hydrate", input);
}

#[test]
fn test_multiple_components() {
let input = r#"
import { useLoaderData } from '@remix-run/react';
export const loader = () => 42;
export default function Route() {
const data = useLoaderData<typeof loader>();
}
function Internal() {
const data = useLoaderData<typeof loader>();
}
export function Exported() {
const data = useLoaderData<typeof loader>();
}
"#;
assert_snapshot("multiple_components", input);
}

#[test]
fn test_unrelated_function_args() {
let input = r#"
Expand Down
1 change: 1 addition & 0 deletions src/codemod_models.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use oxc_span::Span;

#[derive(Debug, Clone)]
pub struct HookDeclarator<'a> {
pub name: &'a str,
pub source_text: &'a str,
Expand Down
22 changes: 22 additions & 0 deletions src/snapshots/multiple_components.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: src/codemod.rs
description: "import { useLoaderData } from '@remix-run/react';\n\nexport const loader = () => 42;\n\nexport default function Route() {\n const data = useLoaderData<typeof loader>();\n}\n\nfunction Internal() {\n const data = useLoaderData<typeof loader>();\n}\n\nexport function Exported() {\n const data = useLoaderData<typeof loader>();\n}\n"
expression: "codemod(&input, source_type).unwrap()"
---
import { useLoaderData } from '@remix-run/react';



function Internal() {
const data = useLoaderData<typeof loader>();
}

export function Exported() {
const data = useLoaderData<typeof loader>();
}

export default defineRoute({
Component({ loaderData: data }) {
},
serverLoader: () => 42,
});

0 comments on commit a760fa6

Please sign in to comment.