Skip to content

Commit c88dd92

Browse files
committed
Revert "feat(static-config): resolve indirect exports via top-level identifier lookup"
This reverts commit ecc3fc2.
1 parent c037f46 commit c88dd92

File tree

1 file changed

+13
-179
lines changed
  • crates/vite_static_config/src

1 file changed

+13
-179
lines changed

crates/vite_static_config/src/lib.rs

Lines changed: 13 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! config like `run` without needing a Node.js runtime.
66
77
use oxc_allocator::Allocator;
8-
use oxc_ast::ast::{BindingPattern, Expression, ObjectPropertyKind, Program, Statement};
8+
use oxc_ast::ast::{Expression, ObjectPropertyKind, Program, Statement};
99
use oxc_parser::Parser;
1010
use oxc_span::SourceType;
1111
use rustc_hash::FxHashMap;
@@ -110,39 +110,19 @@ fn parse_js_ts_config(source: &str, extension: &str) -> StaticConfig {
110110
extract_config_fields(&result.program)
111111
}
112112

113-
/// Scan top-level statements for a `const`/`let`/`var` declarator whose simple
114-
/// binding identifier matches `name`, and return a reference to its initializer.
115-
///
116-
/// Returns `None` if no match is found or the declarator has no initializer.
117-
/// Destructured bindings (object/array patterns) are intentionally skipped.
118-
fn find_top_level_init<'a>(name: &str, stmts: &'a [Statement<'a>]) -> Option<&'a Expression<'a>> {
119-
for stmt in stmts {
120-
let Statement::VariableDeclaration(decl) = stmt else { continue };
121-
for declarator in &decl.declarations {
122-
let BindingPattern::BindingIdentifier(ident) = &declarator.id else { continue };
123-
if ident.name == name {
124-
return declarator.init.as_ref();
125-
}
126-
}
127-
}
128-
None
129-
}
130-
131113
/// Find the config object in a parsed program and extract its fields.
132114
///
133115
/// Searches for the config value in the following patterns (in order):
134116
/// 1. `export default defineConfig({ ... })`
135117
/// 2. `export default { ... }`
136-
/// 3. `export default config` where `config` is a top-level variable
137-
/// 4. `module.exports = defineConfig({ ... })`
138-
/// 5. `module.exports = { ... }`
139-
/// 6. `module.exports = config` where `config` is a top-level variable
140-
fn extract_config_fields<'a>(program: &'a Program<'a>) -> StaticConfig {
118+
/// 3. `module.exports = defineConfig({ ... })`
119+
/// 4. `module.exports = { ... }`
120+
fn extract_config_fields(program: &Program<'_>) -> StaticConfig {
141121
for stmt in &program.body {
142122
// ESM: export default ...
143123
if let Statement::ExportDefaultDeclaration(decl) = stmt {
144124
if let Some(expr) = decl.declaration.as_expression() {
145-
return extract_config_from_expr(expr, &program.body);
125+
return extract_config_from_expr(expr);
146126
}
147127
// export default class/function — not analyzable
148128
return None;
@@ -155,7 +135,7 @@ fn extract_config_fields<'a>(program: &'a Program<'a>) -> StaticConfig {
155135
m.object().is_specific_id("module") && m.static_property_name() == Some("exports")
156136
})
157137
{
158-
return extract_config_from_expr(&assign.right, &program.body);
138+
return extract_config_from_expr(&assign.right);
159139
}
160140
}
161141

@@ -168,12 +148,8 @@ fn extract_config_fields<'a>(program: &'a Program<'a>) -> StaticConfig {
168148
/// - `defineConfig(() => { return { ... }; })` → extract from return statement
169149
/// - `defineConfig(function() { return { ... }; })` → extract from return statement
170150
/// - `{ ... }` → extract directly
171-
/// - `identifier` → look up in `stmts`, then extract (one level of indirection only)
172151
/// - anything else → not analyzable
173-
fn extract_config_from_expr<'a>(
174-
expr: &'a Expression<'a>,
175-
stmts: &'a [Statement<'a>],
176-
) -> StaticConfig {
152+
fn extract_config_from_expr(expr: &Expression<'_>) -> StaticConfig {
177153
let expr = expr.without_parentheses();
178154
match expr {
179155
Expression::CallExpression(call) => {
@@ -185,21 +161,15 @@ fn extract_config_from_expr<'a>(
185161
match first_arg_expr {
186162
Expression::ObjectExpression(obj) => Some(extract_object_fields(obj)),
187163
Expression::ArrowFunctionExpression(arrow) => {
188-
extract_config_from_function_body(&arrow.body, stmts)
164+
extract_config_from_function_body(&arrow.body)
189165
}
190166
Expression::FunctionExpression(func) => {
191-
extract_config_from_function_body(func.body.as_ref()?, stmts)
167+
extract_config_from_function_body(func.body.as_ref()?)
192168
}
193169
_ => None,
194170
}
195171
}
196172
Expression::ObjectExpression(obj) => Some(extract_object_fields(obj)),
197-
// Resolve a top-level identifier to its initializer (one level of indirection).
198-
// Pass empty stmts on the recursive call to prevent chaining (const a = b; export default a).
199-
Expression::Identifier(ident) if !stmts.is_empty() => {
200-
let init = find_top_level_init(&ident.name, stmts)?;
201-
extract_config_from_expr(init, &[])
202-
}
203173
_ => None,
204174
}
205175
}
@@ -212,13 +182,7 @@ fn extract_config_from_expr<'a>(
212182
///
213183
/// Returns `None` (not analyzable) if the body contains multiple `return` statements
214184
/// (at any nesting depth), since the returned config would depend on runtime control flow.
215-
///
216-
/// `module_stmts` is the program's top-level statement list, used as a fallback when
217-
/// resolving an identifier in a `return <identifier>` statement.
218-
fn extract_config_from_function_body<'a>(
219-
body: &'a oxc_ast::ast::FunctionBody<'a>,
220-
module_stmts: &'a [Statement<'a>],
221-
) -> StaticConfig {
185+
fn extract_config_from_function_body(body: &oxc_ast::ast::FunctionBody<'_>) -> StaticConfig {
222186
// Reject functions with multiple returns — the config depends on control flow.
223187
if count_returns_in_stmts(&body.statements) > 1 {
224188
return None;
@@ -228,19 +192,10 @@ fn extract_config_from_function_body<'a>(
228192
match stmt {
229193
Statement::ReturnStatement(ret) => {
230194
let arg = ret.argument.as_ref()?;
231-
match arg.without_parentheses() {
232-
Expression::ObjectExpression(obj) => return Some(extract_object_fields(obj)),
233-
Expression::Identifier(ident) => {
234-
// Look for the binding in the function body first, then at module level.
235-
let init = find_top_level_init(&ident.name, &body.statements)
236-
.or_else(|| find_top_level_init(&ident.name, module_stmts))?;
237-
if let Expression::ObjectExpression(obj) = init.without_parentheses() {
238-
return Some(extract_object_fields(obj));
239-
}
240-
return None;
241-
}
242-
_ => return None,
195+
if let Expression::ObjectExpression(obj) = arg.without_parentheses() {
196+
return Some(extract_object_fields(obj));
243197
}
198+
return None;
244199
}
245200
Statement::ExpressionStatement(expr_stmt) => {
246201
// Concise arrow: `() => ({ ... })` is represented as ExpressionStatement
@@ -1044,125 +999,4 @@ mod tests {
1044999
);
10451000
assert_json(&result, "run", serde_json::json!({ "cacheScripts": true }));
10461001
}
1047-
1048-
// ── Indirect exports (identifier resolution) ─────────────────────────
1049-
1050-
#[test]
1051-
fn export_default_identifier_object() {
1052-
let result = parse(
1053-
r"
1054-
const config = { run: { cacheScripts: true } };
1055-
export default config;
1056-
",
1057-
);
1058-
assert_json(&result, "run", serde_json::json!({ "cacheScripts": true }));
1059-
}
1060-
1061-
#[test]
1062-
fn export_default_identifier_define_config() {
1063-
// Real-world tanstack-start-helloworld pattern
1064-
let result = parse(
1065-
r"
1066-
import { defineConfig } from 'vite-plus';
1067-
const config = defineConfig({
1068-
run: { cacheScripts: true },
1069-
plugins: [devtools(), nitro()],
1070-
});
1071-
export default config;
1072-
",
1073-
);
1074-
assert_json(&result, "run", serde_json::json!({ "cacheScripts": true }));
1075-
assert_non_static(&result, "plugins");
1076-
}
1077-
1078-
#[test]
1079-
fn module_exports_identifier_object() {
1080-
let result = parse_js_ts_config(
1081-
r"
1082-
const config = { run: { cache: true } };
1083-
module.exports = config;
1084-
",
1085-
"cjs",
1086-
)
1087-
.expect("expected analyzable config");
1088-
assert_json(&result, "run", serde_json::json!({ "cache": true }));
1089-
}
1090-
1091-
#[test]
1092-
fn module_exports_identifier_define_config() {
1093-
let result = parse_js_ts_config(
1094-
r"
1095-
const { defineConfig } = require('vite-plus');
1096-
const config = defineConfig({ run: { cacheScripts: true } });
1097-
module.exports = config;
1098-
",
1099-
"cjs",
1100-
)
1101-
.expect("expected analyzable config");
1102-
assert_json(&result, "run", serde_json::json!({ "cacheScripts": true }));
1103-
}
1104-
1105-
#[test]
1106-
fn define_config_callback_return_local_identifier() {
1107-
let result = parse(
1108-
r"
1109-
export default defineConfig(({ mode }) => {
1110-
const obj = { run: { cacheScripts: true }, plugins: [vue()] };
1111-
return obj;
1112-
});
1113-
",
1114-
);
1115-
assert_json(&result, "run", serde_json::json!({ "cacheScripts": true }));
1116-
assert_non_static(&result, "plugins");
1117-
}
1118-
1119-
#[test]
1120-
fn define_config_callback_return_module_level_identifier() {
1121-
let result = parse(
1122-
r"
1123-
const shared = { run: { cacheScripts: true } };
1124-
export default defineConfig(() => {
1125-
return shared;
1126-
});
1127-
",
1128-
);
1129-
assert_json(&result, "run", serde_json::json!({ "cacheScripts": true }));
1130-
}
1131-
1132-
#[test]
1133-
fn export_default_identifier_undeclared_is_none() {
1134-
// Identifier not declared in file — not analyzable
1135-
assert!(parse_js_ts_config("export default config;", "ts").is_none());
1136-
}
1137-
1138-
#[test]
1139-
fn export_default_identifier_no_init_is_none() {
1140-
// Variable declared without initializer — not analyzable
1141-
assert!(
1142-
parse_js_ts_config(
1143-
r"
1144-
let config;
1145-
export default config;
1146-
",
1147-
"ts",
1148-
)
1149-
.is_none()
1150-
);
1151-
}
1152-
1153-
#[test]
1154-
fn export_default_chained_identifier_is_none() {
1155-
// Chained indirection (const a = b) — only one level is resolved
1156-
assert!(
1157-
parse_js_ts_config(
1158-
r"
1159-
const inner = { run: {} };
1160-
const config = inner;
1161-
export default config;
1162-
",
1163-
"ts",
1164-
)
1165-
.is_none()
1166-
);
1167-
}
11681002
}

0 commit comments

Comments
 (0)