-
-
Notifications
You must be signed in to change notification settings - Fork 133
Fix TanStack Start compatibility #5756
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ca92bdb
7d59714
db16840
8318f5e
96d9e03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,77 @@ use swc_ecma_ast as ast; | |
|
|
||
| use super::*; | ||
| use crate::ir::*; | ||
| use crate::lower_types::hoisted_text_codec::{ | ||
| infer_hoisted_text_codec_var_type, require_literal_specifier, | ||
| }; | ||
|
|
||
| fn should_enable_react_automatic_jsx(name: &str, ast_module: &ast::Module) -> bool { | ||
| let is_jsx_source = name.ends_with(".tsx") | ||
| || name.ends_with(".jsx") | ||
| || name.contains(".tsx?") | ||
| || name.contains(".jsx?"); | ||
| if !is_jsx_source { | ||
| return false; | ||
| } | ||
|
|
||
| let mut has_explicit_react_import = false; | ||
| let mut has_react_ecosystem_import = false; | ||
| for item in &ast_module.body { | ||
| let ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(import)) = item else { | ||
| continue; | ||
| }; | ||
| let source = import.src.value.to_string_lossy().to_string(); | ||
| if source == "react" && import_has_runtime_binding(import) { | ||
| has_explicit_react_import = true; | ||
| } | ||
|
Comment on lines
+30
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -n --glob '*.{rs,sh,ts,tsx,js,jsx}' \
'import\s+type\b.*from\s+["'\''"]react["'\''"]|from\s+["'\''"]react["'\''"].*\btype\b' .Repository: PerryTS/perry Length of output: 151 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Map the relevant file and inspect the lowering logic.
ast-grep outline crates/perry-hir/src/lower/lower_module_fn.rs --view expanded || true
wc -l crates/perry-hir/src/lower/lower_module_fn.rs
sed -n '1,120p' crates/perry-hir/src/lower/lower_module_fn.rs
# Inspect the AST import fields used by this code path.
rg -n "type_only|ImportSpecifier::Named|ImportSpecifier::Default|ImportSpecifier::Namespace|has_explicit_react_import|react_ecosystem" crates/perry-hir crates -g '*.rs'Repository: PerryTS/perry Length of output: 25056 Ignore type-only React imports here. 🤖 Prompt for AI Agents |
||
| if source.starts_with("@tanstack/react-") | ||
| || source == "@tanstack/react-router" | ||
| || source == "react/jsx-runtime" | ||
| { | ||
| has_react_ecosystem_import = true; | ||
| } | ||
| } | ||
|
|
||
| if has_explicit_react_import { | ||
| return false; | ||
| } | ||
|
|
||
| has_react_ecosystem_import | ||
| || name.contains("node_modules/@tanstack/react-") | ||
| || name.contains("node_modules/@tanstack/react-router/") | ||
| } | ||
|
|
||
| fn import_has_runtime_binding(import: &ast::ImportDecl) -> bool { | ||
| if import.type_only { | ||
| return false; | ||
| } | ||
| import.specifiers.iter().any(|spec| match spec { | ||
| ast::ImportSpecifier::Named(named) => !named.is_type_only, | ||
| ast::ImportSpecifier::Default(_) | ast::ImportSpecifier::Namespace(_) => true, | ||
| }) | ||
| } | ||
|
|
||
| fn enable_react_automatic_jsx(module: &mut Module, ctx: &mut LoweringContext) { | ||
| const LOCAL: &str = "__perry_react_auto"; | ||
| let local = LOCAL.to_string(); | ||
| ctx.register_imported_func(local.clone(), local.clone()); | ||
| ctx.namespace_import_locals.insert(local.clone()); | ||
| ctx.namespace_import_sources | ||
| .insert(local.clone(), "react".to_string()); | ||
| ctx.react_default_import_local = Some(local.clone()); | ||
| module.imports.push(Import { | ||
| source: "react".to_string(), | ||
| specifiers: vec![ImportSpecifier::Namespace { local }], | ||
| is_native: false, | ||
| module_kind: ModuleKind::NativeCompiled, | ||
| resolved_path: None, | ||
| type_only: false, | ||
| is_dynamic: false, | ||
| is_dynamic_target: false, | ||
| is_deferred_require: false, | ||
| is_adopted_require: false, | ||
| }); | ||
| } | ||
|
|
||
| fn module_has_strict_mode(ast_module: &ast::Module) -> bool { | ||
| for item in &ast_module.body { | ||
|
|
@@ -355,6 +426,9 @@ pub fn lower_module_full( | |
| ctx.seed_imported_class_accessors(seed); | ||
| } | ||
| let mut module = Module::new(name); | ||
| if should_enable_react_automatic_jsx(name, ast_module) { | ||
| enable_react_automatic_jsx(&mut module, &mut ctx); | ||
| } | ||
|
|
||
| // Pre-scan for `new Function` / `Function(...)` constant-argument | ||
| // resolution: single-assignment module vars, `toString`-bearing object | ||
|
|
@@ -536,6 +610,7 @@ pub fn lower_module_full( | |
| _ => None, | ||
| }; | ||
| if let Some(var_decl) = var_decl { | ||
| let mut builtin_aliases_in_decl = HashSet::new(); | ||
| for decl in &var_decl.decls { | ||
| // #4461: `var X = class { ... }` is lowered as a class | ||
| // expression bound to the name `X` (see stmt.rs) — the class | ||
|
|
@@ -551,12 +626,24 @@ pub fn lower_module_full( | |
| } | ||
| if let ast::Pat::Ident(ident) = &decl.name { | ||
| let name = ident.id.sym.to_string(); | ||
| if decl.init.as_deref().and_then(require_literal_specifier) == Some("util") | ||
| || decl.init.as_deref().and_then(require_literal_specifier) | ||
| == Some("node:util") | ||
| { | ||
| builtin_aliases_in_decl.insert(name.clone()); | ||
| } | ||
| if ctx.lookup_local(&name).is_none() { | ||
| let ty = ident | ||
| .type_ann | ||
| .as_ref() | ||
| .map(|ann| extract_ts_type(&ann.type_ann)) | ||
| .unwrap_or(Type::Any); | ||
| let ty = infer_hoisted_text_codec_var_type(decl, ident, |name| { | ||
| builtin_aliases_in_decl.contains(name) | ||
| || matches!( | ||
| ctx.lookup_builtin_module_alias(name), | ||
| Some("util" | "node:util") | ||
| ) | ||
| || matches!( | ||
| ctx.lookup_native_module(name), | ||
| Some(("util" | "node:util", None)) | ||
| ) | ||
| }); | ||
| ctx.define_local(name.clone(), ty); | ||
| ctx.pre_registered_module_vars.insert(name); | ||
| if var_decl.kind == ast::VarDeclKind::Var { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -391,7 +391,7 @@ pub(crate) fn lower_module_decl( | |||||||||||||||||||||||||||||||||||||
| // so JSX in this module lowers to | ||||||||||||||||||||||||||||||||||||||
| // `<local>.createElement(...)` instead of Perry's | ||||||||||||||||||||||||||||||||||||||
| // eager `js_jsx` adapter (see jsx.rs). | ||||||||||||||||||||||||||||||||||||||
| if source == "react" { | ||||||||||||||||||||||||||||||||||||||
| if source == "react" && !whole_decl_type_only { | ||||||||||||||||||||||||||||||||||||||
| ctx.react_default_import_local = Some(local.clone()); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -422,6 +422,15 @@ pub(crate) fn lower_module_decl( | |||||||||||||||||||||||||||||||||||||
| // not lower to StaticMethodCall — see the heuristic | ||||||||||||||||||||||||||||||||||||||
| // in expr_call::static_and_instance. | ||||||||||||||||||||||||||||||||||||||
| ctx.namespace_import_locals.insert(local.clone()); | ||||||||||||||||||||||||||||||||||||||
| // React namespace imports are the common TSX shape | ||||||||||||||||||||||||||||||||||||||
| // (`import * as React from "react"`). They need the | ||||||||||||||||||||||||||||||||||||||
| // same non-eager React element semantics as default | ||||||||||||||||||||||||||||||||||||||
| // React imports; Perry's native JSX adapter calls | ||||||||||||||||||||||||||||||||||||||
| // function components immediately and therefore runs | ||||||||||||||||||||||||||||||||||||||
| // hooks outside the reconciler. | ||||||||||||||||||||||||||||||||||||||
| if source == "react" && !whole_decl_type_only { | ||||||||||||||||||||||||||||||||||||||
| ctx.react_default_import_local = Some(local.clone()); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+425
to
+433
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -n --glob '*.{rs,sh,ts,tsx,js,jsx}' \
'import\s+type\s+\*\s+as\s+\w+\s+from\s+["'\''"]react["'\''"]' .Repository: PerryTS/perry Length of output: 151 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Locate the relevant file and surrounding code.
FILE="crates/perry-hir/src/lower/module_decl.rs"
echo "== Outline =="
ast-grep outline "$FILE" --view expanded || true
echo
echo "== Relevant lines around 360-470 =="
sed -n '360,470p' "$FILE" | cat -n
echo
echo "== Search for whole_decl_type_only usage =="
rg -n 'whole_decl_type_only|react_default_import_local|namespace import|import type \* as' "$FILE" .Repository: PerryTS/perry Length of output: 50369 Exclude type-only React namespace imports from the runtime JSX binding. Proposed fix- if source == "react" {
+ if source == "react" && !whole_decl_type_only {
ctx.react_default_import_local = Some(local.clone());
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| // Remember the source so a later bare `export { local }` | ||||||||||||||||||||||||||||||||||||||
| // re-exports the namespace itself rather than a bare | ||||||||||||||||||||||||||||||||||||||
| // function symbol (see the local-export branch below). | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.