Skip to content

Commit b723478

Browse files
committed
feat: warn for unknown env property
1 parent d0b745d commit b723478

4 files changed

Lines changed: 65 additions & 2 deletions

File tree

crates/rspack_plugin_javascript/src/parser_plugin/import_meta_plugin.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rspack_core::{
88
RscModuleType, RuntimeGlobals, RuntimeRequirementsDependency, property_access,
99
};
1010
use rspack_error::{Error, Severity};
11-
use rspack_util::SpanExt;
11+
use rspack_util::{SpanExt, fx_hash::FxDashSet};
1212
use swc_atoms::Atom;
1313
use swc_experimental_ecma_ast::{
1414
AssignExpr, CallExpr, Expr, GetSpan, MemberExpr, MemberProp, MetaPropKind, Span, UnaryExpr,
@@ -340,6 +340,7 @@ pub struct ImportMetaPlugin {
340340
import_meta: ArcComputed<ResolvedModuleOptions, ImportMeta>,
341341
recurse_env: AtomicBool,
342342
recurse_env_typeof: AtomicBool,
343+
unknown_env_warning_spans: FxDashSet<(u32, u32)>,
343344
}
344345

345346
impl ImportMetaPlugin {
@@ -348,6 +349,7 @@ impl ImportMetaPlugin {
348349
import_meta,
349350
recurse_env: AtomicBool::new(false),
350351
recurse_env_typeof: AtomicBool::new(false),
352+
unknown_env_warning_spans: Default::default(),
351353
}
352354
}
353355

@@ -382,6 +384,32 @@ impl ImportMetaPlugin {
382384
&& self.known_property_enabled(ImportMetaKnownProperties::ENV)
383385
}
384386

387+
fn add_undefined_import_meta_env_warning(
388+
&self,
389+
parser: &mut JavascriptParser,
390+
for_name: &str,
391+
member_expr_info: Option<&ExpressionExpressionInfo>,
392+
start: u32,
393+
end: u32,
394+
) {
395+
if !self.unknown_env_warning_spans.insert((start, end)) {
396+
return;
397+
}
398+
399+
let name = member_expr_info.map_or_else(
400+
|| for_name.to_string(),
401+
|info| concat_string!("import.meta", property_access(info.members.iter(), 0)),
402+
);
403+
let mut warning = create_traceable_error(
404+
"Undefined environment variable".into(),
405+
format!("Environment variable {name} is not defined."),
406+
parser.source.to_string(),
407+
(start, end).into(),
408+
);
409+
warning.severity = Severity::Warning;
410+
parser.add_warning(warning.into());
411+
}
412+
385413
fn evaluate_import_meta_env_identifier<'p>(
386414
&self,
387415
parser: &mut JavascriptParser<'p>,
@@ -399,6 +427,7 @@ impl ImportMetaPlugin {
399427
let Some(code) =
400428
member_expr_info.and_then(|info| render_import_meta_env_expression_info(parser, info, start))
401429
else {
430+
self.add_undefined_import_meta_env_warning(parser, for_name, member_expr_info, start, end);
402431
return Some(eval::evaluate_to_undefined(start, end));
403432
};
404433
if self.recurse_env.swap(true, Ordering::Acquire) {
@@ -431,6 +460,7 @@ impl ImportMetaPlugin {
431460
let Some(code) =
432461
member_expr_info.and_then(|info| render_import_meta_env_expression_info(parser, info, start))
433462
else {
463+
self.add_undefined_import_meta_env_warning(parser, for_name, member_expr_info, start, end);
434464
return Some(eval::evaluate_to_string(
435465
"undefined".to_string(),
436466
start,
@@ -937,6 +967,18 @@ impl<'p, 'a> JavascriptParserPlugin<'p, 'a> for ImportMetaPlugin {
937967
return None;
938968
}
939969

970+
if let Some(MemberExpressionInfo::Expression(info)) =
971+
parser.get_member_expression_info(ExprRef::Member(expr), AllowedMemberTypes::Expression)
972+
{
973+
self.evaluate_import_meta_env_identifier(
974+
parser,
975+
&info.name,
976+
Some(&info),
977+
expr.span().real_lo(),
978+
expr.span().real_hi(),
979+
);
980+
}
981+
940982
add_import_meta_env_value_dependency(parser);
941983
let replacement = render_import_meta_env_member_chain(
942984
parser,
@@ -1040,6 +1082,13 @@ impl<'p, 'a> JavascriptParserPlugin<'p, 'a> for ImportMetaPlugin {
10401082
.first()
10411083
.is_some_and(|member| member == "env")
10421084
{
1085+
self.evaluate_import_meta_env_identifier(
1086+
parser,
1087+
&members.name,
1088+
Some(members),
1089+
expr.span().real_lo(),
1090+
expr.span().real_hi(),
1091+
);
10431092
add_import_meta_env_value_dependency(parser);
10441093
let replacement = render_import_meta_env_member_chain(
10451094
parser,

tests/rspack-test/configCases/plugins/import-meta-env/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ it("should treat import.meta.env.NOT_EXIST as falsy", () => {
7676
}
7777
});
7878

79+
it("should warn when typeof accesses an undefined env field", () => {
80+
expect(typeof import.meta.env.TYPEOF_UNKNOWN).toBe("undefined");
81+
});
82+
83+
it("should not warn for an env field explicitly defined as undefined", () => {
84+
expect(import.meta.env.EXPLICIT_UNDEFINED).toBe(undefined);
85+
});
86+
7987
it("should preserve whole-object import.meta.env definitions", () => {
8088
const env = import.meta.env;
8189
expect(env.MODE).toBe("production");

tests/rspack-test/configCases/plugins/import-meta-env/rspack.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ module.exports = {
3737
VALUE: JSON.stringify('object-form'),
3838
},
3939
'process.env.PROCESS_ONLY': JSON.stringify('process_only'),
40+
'import.meta.env.EXPLICIT_UNDEFINED': 'undefined',
4041
}),
4142
new DefinePlugin({
4243
'import.meta.env.ORDERED_VAR': JSON.stringify('second_define_plugin'),
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
"use strict";
22

33
module.exports = [
4-
/Conflicting values for 'import\.meta\.env\.ORDERED_VAR'/
4+
/Conflicting values for 'import\.meta\.env\.ORDERED_VAR'/,
5+
/Environment variable import\.meta\.env\.NOT_EXIST is not defined\./,
6+
/Environment variable import\.meta\.env\.NOT_EXIST is not defined\./,
7+
/Environment variable import\.meta\.env\.TYPEOF_UNKNOWN is not defined\./,
8+
/Environment variable import\.meta\.env\.DOT\.KEY is not defined\./,
9+
/Environment variable import\.meta\.env\.UNKNOWN is not defined\./
510
];

0 commit comments

Comments
 (0)