Skip to content

Commit

Permalink
feat: support WarnCaseSensitiveModulesPlugin (#4379)
Browse files Browse the repository at this point in the history
* feat: support WarnCaseSensitiveModulesPlugin

* style: clippy

* fix: fix unexpected warning

* test: add sensitive warning tests

* fix: use RegExp error

* fix: change according reviews

* fix: correct warning order
  • Loading branch information
OceanPresentChao authored Nov 6, 2023
1 parent 4b2801b commit b32da51
Show file tree
Hide file tree
Showing 20 changed files with 173 additions and 1 deletion.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/rspack_binding_options/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ rspack_plugin_split_chunks = { path = "../rspack_plugin_split_chunk
rspack_plugin_split_chunks_new = { path = "../rspack_plugin_split_chunks_new" }
rspack_plugin_swc_css_minimizer = { path = "../rspack_plugin_swc_css_minimizer" }
rspack_plugin_swc_js_minimizer = { path = "../rspack_plugin_swc_js_minimizer" }
rspack_plugin_warn_sensitive_module = { path = "../rspack_plugin_warn_sensitive_module" }
rspack_plugin_wasm = { path = "../rspack_plugin_wasm" }
rspack_plugin_web_worker_template = { path = "../rspack_plugin_web_worker_template" }
rspack_plugin_worker = { path = "../rspack_plugin_worker" }
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_binding_options/src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ impl RawOptionsApply for RawOptions {

plugins.push(rspack_plugin_ensure_chunk_conditions::EnsureChunkConditionsPlugin.boxed());

plugins.push(rspack_plugin_warn_sensitive_module::WarnCaseSensitiveModulesPlugin.boxed());

Ok(Self::Options {
context,
mode,
Expand Down
3 changes: 3 additions & 0 deletions crates/rspack_core/src/compiler/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,9 @@ impl Compilation {
pub async fn seal(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> {
let logger = self.get_logger("rspack.Compilation");

// https://github.com/webpack/webpack/blob/main/lib/Compilation.js#L2809
plugin_driver.seal(self)?;

let start = logger.time("optimize dependencies");
// https://github.com/webpack/webpack/blob/d15c73469fd71cf98734685225250148b68ddc79/lib/Compilation.js#L2812-L2814
while plugin_driver.optimize_dependencies(self).await?.is_some() {}
Expand Down
4 changes: 4 additions & 0 deletions crates/rspack_core/src/plugin/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,10 @@ pub trait Plugin: Debug + Send + Sync {
async fn after_emit(&self, _compilation: &mut Compilation) -> Result<()> {
Ok(())
}

fn seal(&self, _compilation: &mut Compilation) -> Result<()> {
Ok(())
}
}

pub type BoxPlugin = Box<dyn Plugin>;
Expand Down
8 changes: 8 additions & 0 deletions crates/rspack_core/src/plugin/plugin_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,4 +631,12 @@ impl PluginDriver {
}
Ok(())
}

#[instrument(name = "plugin:seal", skip_all)]
pub fn seal(&self, compilation: &mut Compilation) -> Result<()> {
for plugin in &self.plugins {
plugin.seal(compilation)?;
}
Ok(())
}
}
11 changes: 11 additions & 0 deletions crates/rspack_plugin_warn_sensitive_module/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
edition = "2021"
license = "MIT"
name = "rspack_plugin_warn_sensitive_module"
repository = "https://github.com/web-infra-dev/rspack"
version = "0.1.0"

[dependencies]
dashmap = { workspace = true }
rspack_core = { path = "../rspack_core" }
rspack_error = { path = "../rspack_error" }
103 changes: 103 additions & 0 deletions crates/rspack_plugin_warn_sensitive_module/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// https://github.com/webpack/webpack/blob/main/lib/WarnCaseSensitiveModulesPlugin.js

use std::collections::HashMap;

use rspack_core::{Compilation, Logger, Module, ModuleGraph, Plugin};
use rspack_error::Diagnostic;

#[derive(Debug, Default)]
pub struct WarnCaseSensitiveModulesPlugin;

impl WarnCaseSensitiveModulesPlugin {
pub fn new() -> Self {
Self
}

pub fn create_sensitive_modules_warning(
&self,
modules: &Vec<&dyn Module>,
graph: &ModuleGraph,
) -> String {
let mut message =
String::from("There are multiple modules with names that only differ in casing.\n");

for m in modules {
if let Some(boxed_m) = graph.module_by_identifier(&m.identifier()) {
let mut module_msg = format!(" - {}\n", m.identifier());
graph
.get_incoming_connections(boxed_m)
.iter()
.for_each(|c| {
if let Some(original_identifier) = c.original_module_identifier {
module_msg.push_str(&format!(" - used by {}\n", original_identifier));
}
});

message.push_str(&module_msg);
}
}

message
}
}

// This Plugin warns when there are case sensitive modules in the compilation
// which can cause unexpected behavior when deployed on a case-insensitive environment
// it is executed in hook `compilation.seal`
impl Plugin for WarnCaseSensitiveModulesPlugin {
fn name(&self) -> &'static str {
"rspack.WarnCaseSensitiveModulesPlugin"
}

fn seal(&self, compilation: &mut Compilation) -> rspack_error::Result<()> {
let logger = compilation.get_logger(self.name());
let start = logger.time("check case sensitive modules");
let mut diagnostics: Vec<Diagnostic> = vec![];
let modules = compilation
.module_graph
.modules()
.values()
.collect::<Vec<_>>();
let mut module_without_case_map: HashMap<String, HashMap<String, &Box<dyn Module>>> =
HashMap::new();

for module in modules {
// Ignore `data:` URLs, because it's not a real path
if let Some(normal_module) = module.as_normal_module() {
if normal_module
.resource_resolved_data()
.encoded_content
.is_some()
{
continue;
}
}

let identifier = module.identifier().to_string();
let lower_identifier = identifier.to_lowercase();
let lower_map = module_without_case_map.entry(lower_identifier).or_default();
lower_map.insert(identifier, module);
}

// sort by module identifier, guarantee the warning order
let mut case_map_vec = module_without_case_map.into_iter().collect::<Vec<_>>();
case_map_vec.sort_by(|a, b| a.0.cmp(&b.0));

for (_, lower_map) in case_map_vec {
if lower_map.values().len() > 1 {
let mut case_modules = lower_map.values().map(|m| m.as_ref()).collect::<Vec<_>>();
case_modules.sort_by_key(|m| m.identifier());
diagnostics.push(Diagnostic::warn(
"Sensitive Modules Warn".to_string(),
self.create_sensitive_modules_warning(&case_modules, &compilation.module_graph),
0,
0,
));
}
}

compilation.push_batch_diagnostic(diagnostics);
logger.time_end(start);
Ok(())
}
}
1 change: 1 addition & 0 deletions crates/rspack_testing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ rspack_plugin_json = { path = "../rspack_plugin_json" }
rspack_plugin_library = { path = "../rspack_plugin_library" }
rspack_plugin_remove_empty_chunks = { path = "../rspack_plugin_remove_empty_chunks" }
rspack_plugin_runtime = { path = "../rspack_plugin_runtime" }
rspack_plugin_warn_sensitive_module = { path = "../rspack_plugin_warn_sensitive_module" }
rspack_plugin_wasm = { path = "../rspack_plugin_wasm" }
rspack_regex = { path = "../rspack_regex" }
rspack_tracing = { path = "../rspack_tracing" }
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_testing/src/test_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,8 @@ impl TestConfig {
// Notice the plugin need to be placed after SplitChunksPlugin
plugins.push(rspack_plugin_remove_empty_chunks::RemoveEmptyChunksPlugin.boxed());

plugins.push(rspack_plugin_warn_sensitive_module::WarnCaseSensitiveModulesPlugin.boxed());

plugins.push(rspack_plugin_javascript::InferAsyncModulesPlugin {}.boxed());
if self.experiments.async_web_assembly {
plugins.push(rspack_plugin_wasm::FetchCompileAsyncWasmPlugin {}.boxed());
Expand Down
3 changes: 3 additions & 0 deletions packages/rspack/tests/Stats.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ describe("Stats", () => {
<t> process module group map: X ms
<t> ensure max size fit: X ms
LOG from rspack.WarnCaseSensitiveModulesPlugin
<t> check case sensitive modules: X ms
LOG from rspack.buildChunkGraph
<t> prepare entrypoints: X ms
<t> process queue: X ms
Expand Down
Empty file.
Empty file.
Empty file.
Empty file.
9 changes: 9 additions & 0 deletions packages/rspack/tests/cases/warnings/case-sensitive/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
it("should return different modules with different casing", done => {
var a = require("./a");
var A = require("./A");
var b = require("./b/file.js");
var B = require("./B/file.js");
expect(a).not.toBe(A);
expect(b).not.toBe(B);
done();
});
14 changes: 14 additions & 0 deletions packages/rspack/tests/cases/warnings/case-sensitive/warnings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// expect 2 warnings
// the sequence of warnings is not guaranteed
module.exports = [
[
/There are multiple modules with names that only differ in casing/,
/case-sensitive.A\.js/,
/case-sensitive.a\.js/
],
[
/There are multiple modules with names that only differ in casing/,
/case-sensitive.B.file\.js/,
/case-sensitive.b.file\.js/
]
];
Empty file.
Empty file.
2 changes: 1 addition & 1 deletion webpack-test/cases/errors/case-sensitive/test.filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ module.exports = function(config) {
return fs.existsSync(path.join(__dirname, "TEST.FILTER.JS"));
};
*/
module.exports = () => {return "https://github.com/web-infra-dev/rspack/issues/4347"}
module.exports = () => true

0 comments on commit b32da51

Please sign in to comment.